[IMP] point_of_sale,*: move kitchen printer logic to point_of_sale

*:pos_epson_printer,pos_epson_printer_restaurant,point_of_sale,
pos_restaurant,pos_hr_restaurant

Before the printers only worked in the pos_restaurant and not in the
point_of_sale. Indeed, the methods managing the printers were located
in the pos_restaurant.

Now all these methods have been moved.
For the community part here are the affected modules:
- The methods in pos_epson_printer_restaurant have been moved to
pos_epson_printer.
- The methods in pos_restaurant have been moved to point_of_sale.
- The module pos_epson_printer_restaurant has been removed as it only
handled printers.

Some of these methods have been adapted/renamed to better suit
the needs.

This branch the first part of the following task: 3224508

closes odoo/odoo#114655

Related: odoo/upgrade#4414
Related: odoo/enterprise#37901
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
David Monnom (moda)
2023-05-03 12:39:17 +02:00
parent 8e4e8eb81a
commit 73aae73f1b
78 changed files with 563 additions and 1759 deletions
+2 -1
View File
@@ -42,8 +42,9 @@
'views/report_saledetails.xml',
'views/point_of_sale_dashboard.xml',
'views/report_invoice.xml',
'views/res_config_settings_views.xml',
'views/pos_printer_view.xml',
'views/pos_ticket_view.xml',
'views/res_config_settings_views.xml',
],
'demo': [
'data/point_of_sale_demo.xml',
@@ -36,6 +36,13 @@
<field name="start_category">True</field>
</record>
<!-- Preparation Printer -->
<record id="preparation_printer" model="pos.printer">
<field name="name">Preparation Printer</field>
<field name="proxy_ip">localhost</field>
<field name="product_categories_ids" eval="[(6, 0, [ref('point_of_sale.pos_category_miscellaneous')])]" />
</record>
<!-- Resource: product.product -->
<record id="stock.product_cable_management_box" model="product.product">
<field name="pos_categ_id" ref="point_of_sale.pos_category_miscellaneous"/>
+1
View File
@@ -24,3 +24,4 @@ from . import pos_payment
from . import pos_payment_method
from . import pos_bill
from . import report_sale_details
from . import pos_printer
+6 -1
View File
@@ -47,6 +47,8 @@ class PosConfig(models.Model):
return self.env.ref('point_of_sale.group_pos_user')
name = fields.Char(string='Point of Sale', required=True, help="An internal identification of the point of sale.")
printer_ids = fields.Many2many('pos.printer', 'pos_config_printer_rel', 'config_id', 'printer_id', string='Order Printers')
is_order_printer = fields.Boolean('Order Printer')
is_installed_account_accountant = fields.Boolean(string="Is the Full Accounting Installed",
compute="_compute_is_installed_account_accountant")
picking_type_id = fields.Many2one(
@@ -402,6 +404,9 @@ class PosConfig(models.Model):
def write(self, vals):
self._reset_default_on_vals(vals)
if ('is_order_printer' in vals and not vals['is_order_printer']):
vals['printer_ids'] = [fields.Command.clear()]
opened_session = self.mapped('session_ids').filtered(lambda s: s.state != 'closed')
if opened_session:
forbidden_fields = []
@@ -484,7 +489,7 @@ class PosConfig(models.Model):
def _force_http(self):
enforce_https = self.env['ir.config_parameter'].sudo().get_param('point_of_sale.enforce_https')
if not enforce_https and self.other_devices:
if not enforce_https and (self.other_devices or self.printer_ids.filtered(lambda pt: pt.printer_type == 'epson_epos')):
return True
return False
+10 -1
View File
@@ -59,6 +59,7 @@ class PosOrder(models.Model):
'tip_amount': ui_order.get('tip_amount', 0),
'access_token': ui_order.get('access_token', ''),
'ticket_code': ui_order.get('ticket_code', ''),
'last_order_preparation_change': ui_order.get('last_order_preparation_change', False),
}
@api.model
@@ -237,6 +238,7 @@ class PosOrder(models.Model):
return price_unit
name = fields.Char(string='Order Ref', required=True, readonly=True, copy=False, default='/')
last_order_preparation_change = fields.Char(string='Last preparation change', help="Last printed state of the order")
date_order = fields.Datetime(string='Date', readonly=True, index=True, default=fields.Datetime.now)
user_id = fields.Many2one(
comodel_name='res.users', string='Responsible',
@@ -280,7 +282,7 @@ class PosOrder(models.Model):
picking_type_id = fields.Many2one('stock.picking.type', related='session_id.config_id.picking_type_id', string="Operation Type", readonly=False)
procurement_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False)
note = fields.Text(string='Kitchen Notes')
note = fields.Text(string='Internal Notes')
nb_print = fields.Integer(string='Number of Print', readonly=True, copy=False, default=0)
pos_reference = fields.Char(string='Receipt Number', readonly=True, copy=False)
sale_journal = fields.Many2one('account.journal', related='session_id.config_id.journal_id', string='Sales Journal', store=True, readonly=True, ondelete='restrict')
@@ -1103,6 +1105,7 @@ class PosOrder(models.Model):
'create_uid',
'create_date',
'fiscal_position_id',
'last_order_preparation_change',
'to_invoice',
'access_token',
'ticket_code',
@@ -1232,7 +1235,9 @@ class PosOrder(models.Model):
"""This function is here to be overriden"""
fields = [
'id',
'uuid',
'discount',
'skip_change',
'product_id',
'price_unit',
'order_id',
@@ -1278,6 +1283,7 @@ class PosOrderLine(models.Model):
company_id = fields.Many2one('res.company', string='Company', related="order_id.company_id", store=True)
name = fields.Char(string='Line No', required=True, copy=False)
skip_change = fields.Boolean('Skip line when sending ticket to kitchen printers.')
notice = fields.Char(string='Discount Notice')
product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], required=True, change_default=True)
price_unit = fields.Float(string='Unit Price', digits=0)
@@ -1303,6 +1309,7 @@ class PosOrderLine(models.Model):
refund_orderline_ids = fields.One2many('pos.order.line', 'refunded_orderline_id', 'Refund Order Lines', help='Orderlines in this field are the lines that refunded this orderline.')
refunded_orderline_id = fields.Many2one('pos.order.line', 'Refunded Order Line', help='If this orderline is a refund, then the refunded orderline is specified in this field.')
refunded_qty = fields.Float('Refunded Quantity', compute='_compute_refund_qty', help='Number of items refunded in this orderline.')
uuid = fields.Char(string='Uuid', readonly=True, copy=False)
@api.depends('refund_orderline_ids')
def _compute_refund_qty(self):
@@ -1402,6 +1409,8 @@ class PosOrderLine(models.Model):
return {
'qty': orderline.qty,
'price_unit': orderline.price_unit,
'skip_change': orderline.skip_change,
'uuid': orderline.uuid,
'price_subtotal': orderline.price_subtotal,
'price_subtotal_incl': orderline.price_subtotal_incl,
'product_id': orderline.product_id.id,
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class PosPrinter(models.Model):
_name = 'pos.printer'
_description = 'Point of Sale Printer'
name = fields.Char('Printer Name', required=True, default='Printer', help='An internal identification of the printer')
printer_type = fields.Selection(string='Printer Type', default='iot',
selection=[('iot', ' Use a printer connected to the IoT Box')])
proxy_ip = fields.Char('Proxy IP Address', help="The IP Address or hostname of the Printer's hardware proxy")
product_categories_ids = fields.Many2many('pos.category', 'printer_category_rel', 'printer_id', 'category_id', string='Printed Product Categories')
@api.constrains('proxy_ip')
def _constrains_proxy_ip(self):
for record in self:
if record.printer_type == 'iot' and not record.proxy_ip:
raise ValidationError(_("Proxy IP cannot be empty."))
@@ -1654,6 +1654,7 @@ class PosSession(models.Model):
'account.tax',
'pos.session',
'pos.config',
'pos.printer',
'pos.bill',
'res.partner',
'stock.picking.type',
@@ -1887,6 +1888,17 @@ class PosSession(models.Model):
def _get_pos_ui_res_currency(self, params):
return self.env['res.currency'].search_read(**params['search_params'])[0]
def _get_pos_ui_pos_printer(self, params):
return self.env['pos.printer'].search_read(**params['search_params'])
def _loader_params_pos_printer(self):
return {
'search_params': {
'domain': [('id', 'in', self.config_id.printer_ids.ids)],
'fields': ['name', 'proxy_ip', 'product_categories_ids', 'printer_type'],
},
}
def _loader_params_pos_category(self):
domain = []
if self.config_id.limit_categories and self.config_id.iface_available_categ_ids:
@@ -45,6 +45,8 @@ class ResConfigSettings(models.TransientModel):
pos_module_pos_discount = fields.Boolean(related='pos_config_id.module_pos_discount', readonly=False)
pos_module_pos_hr = fields.Boolean(related='pos_config_id.module_pos_hr', readonly=False)
pos_module_pos_restaurant = fields.Boolean(related='pos_config_id.module_pos_restaurant', readonly=False)
pos_is_order_printer = fields.Boolean(compute='_compute_pos_printer', store=True, readonly=False)
pos_printer_ids = fields.Many2many(related='pos_config_id.printer_ids', readonly=False)
pos_allowed_pricelist_ids = fields.Many2many('product.pricelist', compute='_compute_pos_allowed_pricelist_ids')
pos_amount_authorized_diff = fields.Float(related='pos_config_id.amount_authorized_diff', readonly=False)
@@ -182,6 +184,13 @@ class ResConfigSettings(models.TransientModel):
def _is_cashdrawer_displayed(self, res_config):
return res_config.pos_iface_print_via_proxy
@api.depends('pos_module_pos_restaurant', 'pos_config_id')
def _compute_pos_printer(self):
for res_config in self:
res_config.update({
'pos_is_order_printer': res_config.pos_config_id.is_order_printer,
})
@api.depends('pos_limit_categories', 'pos_config_id')
def _compute_pos_iface_available_categ_ids(self):
for res_config in self:
@@ -1,4 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_pos_printer_manager,pos.printer.manager,model_pos_printer,point_of_sale.group_pos_manager,1,1,1,1
access_pos_printer,pos.printer.user,model_pos_printer,point_of_sale.group_pos_user,1,0,0,0
access_pos_order,pos.order,model_pos_order,group_pos_user,1,1,1,1
access_pos_order_line,pos.order.line,model_pos_order_line,group_pos_user,1,1,1,1
access_pos_pack_operation_lot,pos.pack.operation.lot,model_pos_pack_operation_lot,group_pos_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_pos_printer_manager pos.printer.manager model_pos_printer point_of_sale.group_pos_manager 1 1 1 1
3 access_pos_printer pos.printer.user model_pos_printer point_of_sale.group_pos_user 1 0 0 0
4 access_pos_order pos.order model_pos_order group_pos_user 1 1 1 1
5 access_pos_order_line pos.order.line model_pos_order_line group_pos_user 1 1 1 1
6 access_pos_pack_operation_lot pos.pack.operation.lot model_pos_pack_operation_lot group_pos_user 1 1 1 1
@@ -56,6 +56,25 @@ export class PosStore extends Reactive {
}
}
// Now the printer should work in PoS without restaurant
async sendOrderInPreparation(order) {
if (this.globalState.printers_category_ids_set.size) {
try {
if (order.hasChangesToPrint()) {
const isPrintSuccessful = await order.printChanges();
if (!isPrintSuccessful) {
this.popup.add(ErrorPopup, {
title: _t("Printing failed"),
body: _t("Failed in printing the changes in the order"),
});
}
}
} catch (e) {
console.warn("Failed in printing the changes in the order", e);
}
}
order.updateLastOrderChange();
}
closeScreen() {
const { name: screenName } = this.globalState.get_order().get_screen_data();
this.showScreen(screenName);
@@ -23,7 +23,7 @@ ProductScreen.addControlButton({
condition: function () {
return (
this.pos.globalState.config.module_pos_restaurant &&
this.pos.globalState.printers_category_ids_set.size
this.pos.globalState.orderPreparationCategories.size
);
},
});
@@ -1,11 +1,34 @@
/** @odoo-module */
import { Component } from "@odoo/owl";
import { usePos } from "@point_of_sale/app/pos_hook";
export class Orderline extends Component {
static template = "Orderline";
/**
* if doubleclick, change hasChange to skipChange
*
* IMPROVEMENT: Instead of handling both double click and click in single
* method, perhaps we can separate double click from single click.
*/
setup() {
this.pos = usePos();
}
selectLine() {
const line = this.props.line; // the orderline
if (this.env.pos.get_order().selected_orderline.id !== line.id) {
this.mp_dbclk_time = new Date().getTime();
} else if (!this.mp_dbclk_time) {
this.mp_dbclk_time = new Date().getTime();
} else if (this.mp_dbclk_time + 500 > new Date().getTime()) {
line.toggleSkipChange();
this.mp_dbclk_time = 0;
} else {
this.mp_dbclk_time = new Date().getTime();
}
this.props.selectLine(this.props.line);
}
lotIconClicked() {
@@ -14,6 +37,10 @@ export class Orderline extends Component {
get addedClasses() {
return {
selected: this.props.line.selected,
"has-change":
this.props.line.hasChange && this.pos.globalState.config.module_pos_restaurant,
"skip-change":
this.props.line.skipChange && this.pos.globalState.config.module_pos_restaurant,
};
}
get customerNote() {
@@ -43,6 +43,12 @@ export class ReceiptScreen extends AbstractReceiptScreen {
await this.handleAutoPrint();
}
}, 0);
// When the order is paid, if there is still a part of the order
// to send in preparation it is automatically sent
if (this.pos.globalState.orderPreparationCategories.size) {
this.pos.sendOrderInPreparation(this.currentOrder);
}
});
}
_addNewOrder() {
+297 -38
View File
@@ -3,9 +3,10 @@
import { PosDB } from "@point_of_sale/js/db";
import { formatFloat } from "@web/views/fields/formatters";
import { uuidv4, batched, deduceUrl } from "@point_of_sale/js/utils";
import { HWPrinter } from "@point_of_sale/app/printer/hw_printer";
// FIXME POSREF - unify use of native parseFloat and web's parseFloat. We probably don't need the native version.
import { parseFloat as oParseFloat } from "@web/views/fields/parsers";
import { batched, uuidv4 } from "@point_of_sale/js/utils";
import { formatDate, formatDateTime, serializeDateTime } from "@web/core/l10n/dates";
import {
roundDecimals as round_di,
@@ -21,7 +22,7 @@ import { sprintf } from "@web/core/utils/strings";
import { Mutex } from "@web/core/utils/concurrency";
import { memoize } from "@web/core/utils/functions";
import { _t } from "@web/core/l10n/translation";
import { renderToString } from "@web/core/utils/render";
import { renderToString, renderToElement } from "@web/core/utils/render";
const { DateTime } = luxon;
@@ -159,6 +160,7 @@ export class PosGlobalState extends PosModel {
this.uom_unit_id = null;
this.default_pricelist = null;
this.order_sequence = 1;
this.printers_category_ids_set = new Set();
// Object mapping the order's name (which contains the uid) to it's server_id after
// validation (order paid then sent to the backend).
@@ -277,6 +279,7 @@ export class PosGlobalState extends PosModel {
this.base_url = loadedData["base_url"];
await this._loadFonts();
await this._loadPictures();
await this._loadPosPrinters(loadedData["pos.printer"]);
}
_loadPosSession() {
// We need to do it here, since only then the local storage has the correct uuid
@@ -286,6 +289,23 @@ export class PosGlobalState extends PosModel {
this.pos_session.sequence_number = Math.max(this.pos_session.sequence_number, ...sequences);
this.pos_session.login_number = odoo.login_number;
}
_loadPosPrinters(printers) {
this.unwatched.printers = [];
// list of product categories that belong to one or more order printer
for (const printerConfig of printers) {
const printer = this.create_printer(printerConfig);
printer.config = printerConfig;
this.unwatched.printers.push(printer);
for (const id of printer.config.product_categories_ids) {
this.printers_category_ids_set.add(id);
}
}
this.config.iface_printers = !!this.unwatched.printers.length;
}
create_printer(config) {
const url = deduceUrl(config.proxy_ip || "");
return new HWPrinter({ rpc: this.env.services.rpc, url });
}
_loadPoSConfig() {
this.db.set_uuid(this.config.uuid);
}
@@ -447,6 +467,12 @@ export class PosGlobalState extends PosModel {
get_cashier_user_id() {
return this.user.id;
}
get orderPreparationCategories() {
if (this.printers_category_ids_set) {
return new Set([...this.printers_category_ids_set]);
}
return new Set();
}
cashierHasPriceControlRights() {
return !this.config.restrict_price_control || this.get_cashier().role == "manager";
}
@@ -730,10 +756,11 @@ export class PosGlobalState extends PosModel {
return message;
}
async _getPricelistJson(pricelistsToGet) {
return await this.orm.call("pos.session", "get_pos_ui_product_pricelists_by_ids", [
[odoo.pos_session_id],
pricelistsToGet,
]);
return await this.env.services.orm.call(
"pos.session",
"get_pos_ui_product_pricelists_by_ids",
[[odoo.pos_session_id], pricelistsToGet]
);
}
_addPosPricelists(pricelistsJson) {
if (!this.config.use_pricelist) {
@@ -772,10 +799,11 @@ export class PosGlobalState extends PosModel {
return message;
}
async _getFiscalPositionJson(fiscalPositionToGet) {
return await this.orm.call("pos.session", "get_pos_ui_account_fiscal_positions_by_ids", [
[odoo.pos_session_id],
fiscalPositionToGet,
]);
return await this.env.services.orm.call(
"pos.session",
"get_pos_ui_account_fiscal_positions_by_ids",
[[odoo.pos_session_id], fiscalPositionToGet]
);
}
_addPosFiscalPosition(fiscalPositionJson) {
this.fiscal_positions.push(...fiscalPositionJson);
@@ -1003,15 +1031,13 @@ export class PosGlobalState extends PosModel {
// - timeout: timeout for the rpc call in ms
// returns a promise that resolves with the list of
// server generated ids for the sent orders
_save_to_server(orders, options) {
async _save_to_server(orders, options) {
if (!orders || !orders.length) {
return Promise.resolve([]);
}
this.set_synch("connecting", orders.length);
options = options || {};
var self = this;
// Keep the order ids that are about to be sent to the
// backend. In between create_from_ui and the success callback
// new orders may have been added to it.
@@ -1023,32 +1049,46 @@ export class PosGlobalState extends PosModel {
// we try to send the order. silent prevents a spinner if it takes too long. (unless we are sending an invoice,
// then we want to notify the user that we are waiting on something )
const orm = options.to_invoice ? this.orm : this.orm.silent;
// FIXME POSREF timeout
// const timeout = typeof options.timeout === "number" ? options.timeout : 30000 * orders.length;
return orm
.call("pos.order", "create_from_ui", [orders, options.draft || false])
.then(function (server_ids) {
order_ids_to_sync.forEach((order_id) => {
self.db.remove_order(order_id);
});
self.failed = false;
self.set_synch("connected");
return server_ids;
})
.catch(function (error) {
console.warn("Failed to send orders:", orders);
if (error.code === 200) {
// Business Logic Error, not a connection problem
// Hide error if already shown before ...
if ((!self.failed || options.show_error) && !options.to_invoice) {
self.failed = error;
self.set_synch("error");
throw error;
}
try {
// FIXME POSREF timeout
// const timeout = typeof options.timeout === "number" ? options.timeout : 30000 * orders.length;
const serverIds = await orm.call("pos.order", "create_from_ui", [
orders,
options.draft || false,
]);
for (const serverId of serverIds) {
const order = this.env.services.pos.globalState.orders.find(
(order) => order.name === serverId.pos_reference
);
if (order) {
order.server_id = serverId.id;
}
self.set_synch("disconnected");
throw error;
});
}
for (const order_id of order_ids_to_sync) {
this.db.remove_order(order_id);
}
this.failed = false;
this.set_synch("connected");
return serverIds;
} catch (error) {
console.warn("Failed to send orders:", orders);
if (error.code === 200) {
// Business Logic Error, not a connection problem
// Hide error if already shown before ...
if ((!this.failed || options.show_error) && !options.to_invoice) {
this.failed = error;
this.set_synch("error");
throw error;
}
}
this.set_synch("disconnected");
throw error;
}
}
// Exports the paid orders (the ones waiting for internet connection)
@@ -1797,6 +1837,8 @@ export class Orderline extends PosModel {
this.pos = options.pos;
this.order = options.order;
this.price_manually_set = options.price_manually_set || false;
this.uuid = this.uuid || uuidv4();
this.price_automatically_set = options.price_automatically_set || false;
if (options.json) {
try {
@@ -1815,6 +1857,9 @@ export class Orderline extends PosModel {
this.set_product_lot(this.product);
this.set_quantity(1);
this.discount = 0;
this.note = "";
this.hasChange = false;
this.skipChange = false;
this.discountStr = "0";
this.selected = false;
this.description = "";
@@ -1854,6 +1899,8 @@ export class Orderline extends PosModel {
this.refunded_qty = json.refunded_qty;
this.refunded_orderline_id = json.refunded_orderline_id;
this.saved_quantity = json.qty;
this.uuid = json.uuid;
this.skipChange = json.skip_change;
}
clone() {
var orderline = new Orderline(
@@ -1934,6 +1981,20 @@ export class Orderline extends PosModel {
this.has_product_lot = product.tracking !== "none";
this.pack_lot_lines = this.has_product_lot && new PosCollection();
}
getNote() {
return this.note;
}
setNote(note) {
this.note = note;
}
toggleSkipChange() {
if (this.hasChange || this.skipChange) {
this.skipChange = !this.skipChange;
}
}
setHasChange(isChange) {
this.hasChange = isChange;
}
// sets a discount [0,100]%
set_discount(discount) {
var parsed_discount =
@@ -2142,6 +2203,8 @@ export class Orderline extends PosModel {
);
// only orderlines of the same product can be merged
return (
!this.skipChange &&
orderline.getNote() === this.getNote() &&
this.get_product().id === orderline.get_product().id &&
this.get_unit() &&
this.get_unit().is_pos_groupable &&
@@ -2172,6 +2235,8 @@ export class Orderline extends PosModel {
});
}
return {
uuid: this.uuid,
skip_change: this.skipChange,
qty: this.get_quantity(),
price_unit: this.get_unit_price(),
price_subtotal: this.get_price_without_tax(),
@@ -2726,6 +2791,8 @@ export class Order extends PosModel {
return fp.id === self.pos.config.default_fiscal_position_id[0];
});
}
this.lastOrderPrepaChange = this.lastOrderPrepaChange || {};
}
save_to_db() {
if (!this.temporary && !this.locked && !this.finalized) {
@@ -2828,6 +2895,8 @@ export class Order extends PosModel {
this.tip_amount = json.tip_amount || 0;
this.access_token = json.access_token || "";
this.ticketCode = json.ticket_code || "";
this.lastOrderPrepaChange =
json.last_order_preparation_change && JSON.parse(json.last_order_preparation_change);
}
export_as_JSON() {
var orderLines, paymentLines;
@@ -2861,6 +2930,7 @@ export class Order extends PosModel {
is_tipped: this.is_tipped || false,
tip_amount: this.tip_amount || 0,
access_token: this.access_token || "",
last_order_preparation_change: JSON.stringify(this.lastOrderPrepaChange),
ticket_code: this.ticketCode || "",
};
if (!this.is_paid && this.user_id) {
@@ -2956,6 +3026,195 @@ export class Order extends PosModel {
return receipt;
}
async printChanges() {
const orderChange = this.changesToOrder;
let isPrintSuccessful = true;
const d = new Date();
let hours = "" + d.getHours();
hours = hours.length < 2 ? "0" + hours : hours;
let minutes = "" + d.getMinutes();
minutes = minutes.length < 2 ? "0" + minutes : minutes;
for (const printer of this.pos.unwatched.printers) {
const changes = this._getPrintingCategoriesChanges(
printer.config.product_categories_ids,
orderChange
);
if (changes["new"].length > 0 || changes["cancelled"].length > 0) {
const printingChanges = {
new: changes["new"],
cancelled: changes["cancelled"],
table_name: this.pos.config.is_table_management ? this.getTable().name : false,
floor_name: this.pos.config.is_table_management
? this.getTable().floor.name
: false,
name: this.name || "unknown order",
time: {
hours,
minutes,
},
};
const receipt = renderToElement("OrderChangeReceipt", { changes: printingChanges });
const result = await printer.printReceipt(receipt);
if (!result.successful) {
isPrintSuccessful = false;
}
}
}
return isPrintSuccessful;
}
_getPrintingCategoriesChanges(categories) {
const currentOrderChange = this.changesToOrder;
return {
new: currentOrderChange["new"].filter((change) =>
this.pos.db.is_product_in_category(categories, change["product_id"])
),
cancelled: currentOrderChange["cancelled"].filter((change) =>
this.pos.db.is_product_in_category(categories, change["product_id"])
),
};
}
/**
* This function is called after the order has been successfully sent to the preparation tool(s).
* In the future, this status should be separated between the different preparation tools,
* so that if one of them returns an error, it is possible to send the information back to it
* without impacting the other tools.
*/
updateLastOrderChange() {
const orderlineIdx = [];
this.orderlines.forEach((line) => {
if (!line.skipChange) {
const note = line.getNote();
const lineKey = `${line.uuid} - ${note}`;
orderlineIdx.push(lineKey);
if (this.lastOrderPrepaChange[lineKey]) {
this.lastOrderPrepaChange[lineKey]["quantity"] = line.get_quantity();
} else {
this.lastOrderPrepaChange[lineKey] = {
line_uuid: line.uuid,
product_id: line.get_product().id,
name: line.get_full_product_name(),
note: note,
quantity: line.get_quantity(),
};
}
line.setHasChange(false);
}
});
// Checks whether an orderline has been deleted from the order since it
// was last sent to the preparation tools. If so we delete it to the changes.
for (const lineKey in this.lastOrderPrepaChange) {
if (!this.getOrderedLine(lineKey)) {
delete this.lastOrderPrepaChange[lineKey];
}
}
}
/**
* @returns {{ [productKey: string]: { product_id: number, name: string, note: string, quantity: number } }}
* This function recalculates the information to be sent to the preparation tools,
* it uses the variable lastOrderPrepaChange which contains the last changes sent
* to perform this calculation.
*/
getOrderChanges() {
const prepaCategoryIds = this.pos.orderPreparationCategories;
const oldChanges = this.lastOrderPrepaChange;
const changes = {};
if (!prepaCategoryIds.size) {
return {};
}
// Compares the orderlines of the order with the last ones sent.
// When one of them has changed, we add the change.
for (const orderlineIdx in this.orderlines) {
const orderline = this.orderlines[orderlineIdx];
if (orderline.skipChange) {
continue;
}
const product = orderline.get_product();
const note = orderline.getNote();
const productKey = `${product.id} - ${orderline.get_full_product_name()} - ${note}`;
const lineKey = `${orderline.uuid} - ${note}`;
if (prepaCategoryIds.has(product.pos_categ_id[0])) {
const quantity = orderline.get_quantity();
const quantityDiff = oldChanges[lineKey]
? quantity - oldChanges[lineKey].quantity
: quantity;
if (quantityDiff) {
changes[productKey] = {
name: orderline.get_full_product_name(),
product_id: product.id,
quantity: quantityDiff,
note: note,
};
orderline.setHasChange(true);
} else {
orderline.setHasChange(false);
}
} else {
orderline.setHasChange(false);
}
}
// Checks whether an orderline has been deleted from the order since it
// was last sent to the preparation tools. If so we add this to the changes.
for (const [lineKey, lineResume] of Object.entries(this.lastOrderPrepaChange)) {
if (!this.getOrderedLine(lineKey)) {
const productKey = `${lineResume["product_id"]} - ${lineResume["name"]} - ${lineResume["note"]}`;
if (!changes[productKey]) {
changes[productKey] = {
product_id: lineResume["product_id"],
name: lineResume["name"],
note: lineResume["note"],
quantity: -lineResume["quantity"],
};
} else {
changes[productKey]["quantity"] -= lineResume["quantity"];
}
}
}
return changes;
}
// This function transforms the data generated by getOrderChanges into the old
// pattern used by the printer and the display preparation. This old pattern comes from
// the time when this logic was in pos_restaurant.
get changesToOrder() {
const toAdd = [];
const toRemove = [];
const changes = Object.values(this.getOrderChanges());
for (const lineChange of changes) {
if (lineChange["quantity"] > 0) {
toAdd.push(lineChange);
} else if (lineChange["quantity"] < 0) {
lineChange["quantity"] *= -1; // we change the sign because that's how it is
toRemove.push(lineChange);
}
}
return { new: toAdd, cancelled: toRemove };
}
getOrderedLine(lineKey) {
return this.orderlines.find(
(line) =>
line.uuid === this.lastOrderPrepaChange[lineKey]["line_uuid"] &&
line.note === this.lastOrderPrepaChange[lineKey]["note"]
);
}
hasSkippedChanges() {
return this.orderlines.find((orderline) => orderline.skipChange) ? true : false;
}
hasChangesToPrint() {
return Object.keys(this.getOrderChanges()).length ? true : false;
}
async pay() {
if (
this.orderlines.some(
@@ -5,6 +5,18 @@
src: url(../fonts/Inconsolata.otf);
}
/* ------ ORDER LINE STATUS ------- */
.pos .order .orderline.has-change {
border-left: solid 6px #6EC89B;
color: #6EC89B;
padding-left: 9px;
}
.pos .order .orderline.skip-change {
border-left: solid 6px #7F82AC;
color: #7F82AC;
padding-left: 9px;
}
/* --- Styling of OpenERP Elements --- */
.ui-dialog, .modal-dialog {
background: white;
@@ -6,6 +6,7 @@ registry.category("mock_server").add("pos.session/load_pos_data", async function
return {
"res.company": { id: 1 },
"pos.session": { id: 1 },
"pos.printer": [],
"pos.config": { id: 1, uuid: "TEST-UUID", trusted_config_ids: [] },
"res.partner": [...(this.models["res.partner"]?.records || [])],
"pos.category": [...(this.models["pos.category"]?.records || [])],
@@ -0,0 +1,49 @@
<odoo>
<record id="view_pos_printer_form" model="ir.ui.view">
<field name="name">Preparation Printer</field>
<field name="model">pos.printer</field>
<field name="arch" type="xml">
<form string="POS Printer">
<sheet>
<group>
<field name="name" />
<field name="printer_type" widget="radio"/>
<field name="proxy_ip" attrs="{'invisible': [('printer_type', '!=', 'iot')]}"/>
<field name="product_categories_ids" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_pos_printer_form" model="ir.actions.act_window">
<field name="name">Preparation Printers</field>
<field name="res_model">pos.printer</field>
<field name="view_mode">tree,kanban,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new restaurant order printer
</p><p>
Order Printers are used by restaurants and bars to print the
order updates in the kitchen/bar when the waiter updates the order.
</p><p>
Each Order Printer has an IP Address that defines the IoT Box/Hardware
Proxy where the printer can be found, and a list of product categories.
An Order Printer will only print updates for products belonging to one of
its categories.
</p>
</field>
</record>
<record id="view_pos_printer" model="ir.ui.view">
<field name="name">Preparation Printers</field>
<field name="model">pos.printer</field>
<field name="arch" type="xml">
<tree string="Preparation Printers">
<field name="name" />
<field name="proxy_ip" />
<field name="product_categories_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
</odoo>
@@ -333,6 +333,18 @@
</div>
</div>
</setting>
<setting string="Preparation Printers" help="Print orders at the kitchen, at the bar, etc." id="is_order_printer">
<field name="pos_is_order_printer"/>
<div class="content-group" attrs="{'invisible': [('pos_is_order_printer', '=', False)]}">
<div class="mt16">
<label string="Printers" for="pos_printer_ids" class="o_light_label"/>
<field name="pos_printer_ids" widget="many2many_tags"/>
</div>
<div>
<button name="%(point_of_sale.action_pos_printer_form)d" icon="fa-arrow-right" type="action" string="Printers" class="btn-link"/>
</div>
</div>
</setting>
<setting string="Preparation Display" help="Display orders on the preparation display" id="preparation_display">
<field name="module_pos_preparation_display"/>
</setting>
+1
View File
@@ -16,6 +16,7 @@ Use Epson ePOS Printers without the IoT Box in the Point of Sale
'data': [
'views/pos_config_views.xml',
'views/res_config_settings_views.xml',
'views/pos_printer_views.xml',
],
'installable': True,
'auto_install': True,
@@ -3,3 +3,5 @@
from . import pos_config
from . import res_config_settings
from . import pos_printer
from . import pos_session
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class PosPrinter(models.Model):
_inherit = 'pos.printer'
printer_type = fields.Selection(selection_add=[('epson_epos', 'Use an Epson printer')])
epson_printer_ip = fields.Char(string='Epson Printer IP Address', help="Local IP address of an Epson receipt printer.", default="0.0.0.0")
@api.constrains('epson_printer_ip')
def _constrains_epson_printer_ip(self):
for record in self:
if record.printer_type == 'epson_epos' and not record.epson_printer_ip:
raise ValidationError(_("Epson Printer IP Address cannot be empty."))
@@ -3,11 +3,10 @@
from odoo import models
class PosSession(models.Model):
_inherit = 'pos.session'
def _loader_params_restaurant_printer(self):
result = super()._loader_params_restaurant_printer()
def _loader_params_pos_printer(self):
result = super()._loader_params_pos_printer()
result['search_params']['fields'].append('epson_printer_ip')
return result
@@ -13,4 +13,11 @@ patch(PosGlobalState.prototype, "pos_epson_printer.PosGlobalState", {
}
});
},
create_printer(config) {
if (config.printer_type === "epson_epos") {
return new EpsonPrinter({ ip: config.epson_printer_ip });
} else {
return this._super(...arguments);
}
},
});
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_restaurant_printer_iot_form" model="ir.ui.view">
<field name="name">pos.restaurant.iot.config.form.view</field>
<field name="model">restaurant.printer</field>
<field name="inherit_id" ref="pos_restaurant.view_restaurant_printer_form"/>
<record id="view_pos_printer_form" model="ir.ui.view">
<field name="name">pos.iot.config.form.view</field>
<field name="model">pos.printer</field>
<field name="inherit_id" ref="point_of_sale.view_pos_printer_form"/>
<field name="arch" type="xml">
<field name="printer_type" position="after">
<field name="epson_printer_ip" attrs="{'invisible': [('printer_type', '!=', 'epson_epos')]}"/>
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'POS Epson Printer Restaurant',
'version': '1.0',
'category': 'Sales/Point of Sale',
'sequence': 6,
'summary': 'Epson Printers as Order Printers',
'description': """
Use Epson Printers as Order Printers in the Point of Sale without the IoT Box
""",
'depends': ['pos_epson_printer', 'pos_restaurant'],
'data': [
'views/pos_restaurant_views.xml',
],
'installable': True,
'auto_install': True,
'assets': {
'point_of_sale.assets': [
'pos_epson_printer_restaurant/static/**/*',
],
},
'license': 'LGPL-3',
}
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Ali Alrehawi <alrehawi@hotmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Ali Alrehawi <alrehawi@hotmail.com>, 2019\n"
"Language-Team: Arabic (https://www.transifex.com/odoo/teams/41243/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "طابعة مطعم"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# trendspotter <j.podhorecky@volny.cz>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: trendspotter <j.podhorecky@volny.cz>, 2019\n"
"Language-Team: Czech (https://www.transifex.com/odoo/teams/41243/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Tiskárna restaurace"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Danish (https://www.transifex.com/odoo/teams/41243/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Chris Egal <sodaswed@web.de>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Chris Egal <sodaswed@web.de>, 2019\n"
"Language-Team: German (https://www.transifex.com/odoo/teams/41243/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Restaurant-Drucker"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Jon Perez <jop@odoo.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Jon Perez <jop@odoo.com>, 2019\n"
"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr "Dirección IP de la impresora de recibos Epson"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Dirección IP local de una impresora de recibos Epson."
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "Tipo de impresora"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Impresora de restaurante"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "Use una impresora Epson"
@@ -1,46 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Nicolas Roussey <nro@odoo.com>, 2019
# thomas quertinmont <tqu@odoo.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: thomas quertinmont <tqu@odoo.com>, 2019\n"
"Language-Team: French (https://www.transifex.com/odoo/teams/41243/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr "Adresse IP de l'imprimante de reçus Epson"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Adresse IP locale d'une imprimante de reçus Epson."
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "Type d'imprimante"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Imprimante du restaurant"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "Utiliser une imprimante Epson"
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Hebrew (https://www.transifex.com/odoo/teams/41243/he/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: he\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Croatian (https://www.transifex.com/odoo/teams/41243/hr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: hr\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Hungarian (https://www.transifex.com/odoo/teams/41243/hu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,46 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Sergio Zanchetta <primes2h@gmail.com>, 2019
# Luigia Cimmino Caserta <lcc@odoo.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Luigia Cimmino Caserta <lcc@odoo.com>, 2019\n"
"Language-Team: Italian (https://www.transifex.com/odoo/teams/41243/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr "Indirizzo IP stampante ricevute Epson"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Indirizzo IP locale stampante ricevute Epson."
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "Tipo stampante"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Stampante ristorante"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "Usa una stampante Epson"
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Luxembourgish (https://www.transifex.com/odoo/teams/41243/lb/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: lb\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Linas Versada <linaskrisiukenas@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Linas Versada <linaskrisiukenas@gmail.com>, 2019\n"
"Language-Team: Lithuanian (https://www.transifex.com/odoo/teams/41243/lt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: lt\n"
"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Restorano spausdintuvas"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Mongolian (https://www.transifex.com/odoo/teams/41243/mn/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: mn\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Norwegian Bokmål (https://www.transifex.com/odoo/teams/41243/nb/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nb\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,46 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Cas Vissers <casvissers@brahoo.nl>, 2019
# Yenthe Van Ginneken <yenthespam@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Yenthe Van Ginneken <yenthespam@gmail.com>, 2019\n"
"Language-Team: Dutch (https://www.transifex.com/odoo/teams/41243/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Lokaal IP adres van een Epson ticket printer."
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "Soort printer"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Restaurant printer"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "Gebruik een Epson printe"
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Polish (https://www.transifex.com/odoo/teams/41243/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pl\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,46 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.5alpha1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-20 09:01+0000\n"
"PO-Revision-Date: 2022-09-20 09:01+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_pos_session
msgid "Point of Sale Session"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Portuguese (https://www.transifex.com/odoo/teams/41243/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/odoo/teams/41243/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Romanian (https://www.transifex.com/odoo/teams/41243/ro/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ro\n"
"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: ru\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Turkish (https://www.transifex.com/odoo/teams/41243/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# Alina Lisnenko <alinasemeniuk1@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Alina Lisnenko <alinasemeniuk1@gmail.com>, 2019\n"
"Language-Team: Ukrainian (https://www.transifex.com/odoo/teams/41243/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: uk\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr "IP-адреса принтера чеків Epson"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Локальна IP-адреса принтера чеків Epson."
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "Тип принтера"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "Ресторанний принтер"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "Використовуйте принтер Epson"
@@ -1,41 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Language-Team: Vietnamese (https://www.transifex.com/odoo/teams/41243/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,46 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# keecome <7017511@qq.com>, 2019
# Felix Yuen <fyu@odoo.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: Felix Yuen <fyu@odoo.com>, 2019\n"
"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr "Epson收据打印机IP地址"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr "Epson收据打印机的本地IP地址。"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr "打印机类型"
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "餐厅打印机"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr "使用Epson打印机"
@@ -1,45 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_epson_printer_restaurant
#
# Translators:
# 敬雲 林 <chingyun@yuanchih-consult.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-26 08:15+0000\n"
"PO-Revision-Date: 2019-08-26 09:13+0000\n"
"Last-Translator: 敬雲 林 <chingyun@yuanchih-consult.com>, 2019\n"
"Language-Team: Chinese (Taiwan) (https://www.transifex.com/odoo/teams/41243/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Epson Receipt Printer IP Address"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,help:pos_epson_printer_restaurant.field_restaurant_printer__epson_printer_ip
msgid "Local IP address of an Epson receipt printer."
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields,field_description:pos_epson_printer_restaurant.field_restaurant_printer__printer_type
msgid "Printer Type"
msgstr ""
#. module: pos_epson_printer_restaurant
#: model:ir.model,name:pos_epson_printer_restaurant.model_restaurant_printer
msgid "Restaurant Printer"
msgstr "餐廳列印機"
#. module: pos_epson_printer_restaurant
#: model:ir.model.fields.selection,name:pos_epson_printer_restaurant.selection__restaurant_printer__printer_type__epson_epos
msgid "Use an Epson printer"
msgstr ""
@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import pos_restaurant
from . import pos_session
@@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class RestaurantPrinter(models.Model):
_inherit = 'restaurant.printer'
printer_type = fields.Selection(selection_add=[('epson_epos', 'Use an Epson printer')])
epson_printer_ip = fields.Char(string='Epson Printer IP Address', help="Local IP address of an Epson receipt printer.")
@@ -1,20 +0,0 @@
/** @odoo-module */
import { PosGlobalState } from "@point_of_sale/js/models";
import { EpsonPrinter } from "@pos_epson_printer/js/epson_printer";
import { patch } from "@web/core/utils/patch";
// The override of create_printer needs to happen after its declaration in
// pos_restaurant. We need to make sure that this code is executed after the
// models file in pos_restaurant.
import "@pos_restaurant/js/models";
patch(PosGlobalState.prototype, "pos_epson_printer_restaurant.PosGlobalState", {
create_printer(config) {
if (config.printer_type === "epson_epos") {
return new EpsonPrinter({ ip: config.epson_printer_ip });
} else {
return this._super(...arguments);
}
},
});
@@ -7,11 +7,6 @@ from odoo import models, api
class PosOrder(models.Model):
_inherit = "pos.order"
def _get_fields_for_draft_order(self):
fields = super()._get_fields_for_draft_order()
fields.append('employee_id')
return fields
@api.model
def get_table_draft_orders(self, table_ids):
table_orders = super().get_table_draft_orders(table_ids)
@@ -3,14 +3,6 @@
<!-- ****** Basic Restaurant Setup ***** -->
<!-- Kitchen Printer -->
<record id="kitchen_printer" model="restaurant.printer">
<field name="name">Kitchen Printer</field>
<field name="proxy_ip">localhost</field>
<field name="product_categories_ids" eval="[(6, 0, [ref('point_of_sale.pos_category_miscellaneous')])]" />
</record>
<record id="drinks" model="pos.category">
<field name="name">Drinks</field>
</record>
+1 -11
View File
@@ -9,20 +9,12 @@ class PosConfig(models.Model):
iface_splitbill = fields.Boolean(string='Bill Splitting', help='Enables Bill Splitting in the Point of Sale.')
iface_printbill = fields.Boolean(string='Bill Printing', help='Allows to print the Bill before payment.')
iface_orderline_notes = fields.Boolean(string='Kitchen Notes', help='Allow custom kitchen notes on Orderlines.', default=True)
iface_orderline_notes = fields.Boolean(string='Internal Notes', help='Allow custom Internal notes on Orderlines.', default=True)
floor_ids = fields.Many2many('restaurant.floor', string='Restaurant Floors', help='The restaurant floors served by this point of sale.')
printer_ids = fields.Many2many('restaurant.printer', 'pos_config_printer_rel', 'config_id', 'printer_id', string='Order Printers')
is_table_management = fields.Boolean('Floors & Tables')
is_order_printer = fields.Boolean('Order Printer')
set_tip_after_payment = fields.Boolean('Set Tip After Payment', help="Adjust the amount authorized by payment terminals to add a tip after the customers left or at the end of the day.")
module_pos_restaurant = fields.Boolean(default=True)
def _force_http(self):
enforce_https = self.env['ir.config_parameter'].sudo().get_param('point_of_sale.enforce_https')
if not enforce_https and self.printer_ids.filtered(lambda pt: pt.printer_type == 'epson_epos'):
return True
return super(PosConfig, self)._force_http()
def get_tables_order_count(self):
""" """
self.ensure_one()
@@ -46,8 +38,6 @@ class PosConfig(models.Model):
def write(self, vals):
if ('is_table_management' in vals and vals['is_table_management'] == False):
vals['floor_ids'] = [(5, 0, 0)]
if ('is_order_printer' in vals and vals['is_order_printer'] == False):
vals['printer_ids'] = [(5, 0, 0)]
return super(PosConfig, self).write(vals)
@api.model
+1 -8
View File
@@ -12,9 +12,7 @@ from odoo import api, fields, models
class PosOrderLine(models.Model):
_inherit = 'pos.order.line'
note = fields.Char('Kitchen Note added by the waiter.')
uuid = fields.Char(string='Uuid', readonly=True, copy=False)
mp_skip = fields.Boolean('Skip line when sending ticket to kitchen printers.')
note = fields.Char('Internal Note added by the waiter.')
class PosOrder(models.Model):
@@ -22,14 +20,11 @@ class PosOrder(models.Model):
table_id = fields.Many2one('restaurant.table', string='Table', help='The table where this order was served', index='btree_not_null', readonly=True)
customer_count = fields.Integer(string='Guests', help='The amount of customers that have been served by this order.', readonly=True)
multiprint_resume = fields.Char(string='Multiprint Resume', help="Last printed state of the order")
def _get_fields_for_order_line(self):
fields = super(PosOrder, self)._get_fields_for_order_line()
fields.extend([
'note',
'uuid',
'mp_skip',
'full_product_name',
'customer_note',
'price_extra',
@@ -42,7 +37,6 @@ class PosOrder(models.Model):
fields.extend([
'table_id',
'customer_count',
'multiprint_resume',
])
return fields
@@ -126,7 +120,6 @@ class PosOrder(models.Model):
order_fields = super(PosOrder, self)._order_fields(ui_order)
order_fields['table_id'] = ui_order.get('table_id', False)
order_fields['customer_count'] = ui_order.get('customer_count', 0)
order_fields['multiprint_resume'] = ui_order.get('multiprint_resume', False)
return order_fields
def _export_for_ui(self, order):
@@ -118,15 +118,3 @@ class RestaurantTable(models.Model):
error_msg = _("You cannot remove a table that is used in a PoS session, close the session(s) first.")
if confs:
raise UserError(error_msg)
class RestaurantPrinter(models.Model):
_name = 'restaurant.printer'
_description = 'Restaurant Printer'
name = fields.Char('Printer Name', required=True, default='Printer', help='An internal identification of the printer')
printer_type = fields.Selection(string='Printer Type', default='iot',
selection=[('iot', ' Use a printer connected to the IoT Box')])
proxy_ip = fields.Char('Proxy IP Address', help="The IP Address or hostname of the Printer's hardware proxy")
product_categories_ids = fields.Many2many('pos.category', 'printer_category_rel', 'printer_id', 'category_id', string='Printed Product Categories')
+2 -14
View File
@@ -10,10 +10,8 @@ class PosSession(models.Model):
def _pos_ui_models_to_load(self):
result = super()._pos_ui_models_to_load()
if self.config_id.module_pos_restaurant:
result.append('restaurant.printer')
if self.config_id.is_table_management:
result.append('restaurant.floor')
if self.config_id.module_pos_restaurant and self.config_id.is_table_management:
result.append('restaurant.floor')
return result
def _loader_params_restaurant_floor(self):
@@ -52,15 +50,5 @@ class PosSession(models.Model):
return floors
def _loader_params_restaurant_printer(self):
return {
'search_params': {
'domain': [('id', 'in', self.config_id.printer_ids.ids)],
'fields': ['name', 'proxy_ip', 'product_categories_ids', 'printer_type'],
},
}
def _get_pos_ui_restaurant_printer(self, params):
return self.env['restaurant.printer'].search_read(**params['search_params'])
def get_pos_ui_restaurant_floor(self):
return self._get_pos_ui_restaurant_floor(self._loader_params_restaurant_floor())
@@ -10,9 +10,7 @@ class ResConfigSettings(models.TransientModel):
pos_iface_orderline_notes = fields.Boolean(compute='_compute_pos_module_pos_restaurant', store=True, readonly=False)
pos_iface_printbill = fields.Boolean(compute='_compute_pos_module_pos_restaurant', store=True, readonly=False)
pos_iface_splitbill = fields.Boolean(compute='_compute_pos_module_pos_restaurant', store=True, readonly=False)
pos_is_order_printer = fields.Boolean(compute='_compute_pos_module_pos_restaurant', store=True, readonly=False)
pos_is_table_management = fields.Boolean(compute='_compute_pos_module_pos_restaurant', store=True, readonly=False)
pos_printer_ids = fields.Many2many(related='pos_config_id.printer_ids', readonly=False)
pos_set_tip_after_payment = fields.Boolean(compute='_compute_pos_set_tip_after_payment', store=True, readonly=False)
@api.depends('pos_module_pos_restaurant', 'pos_config_id')
@@ -23,7 +21,6 @@ class ResConfigSettings(models.TransientModel):
'pos_iface_orderline_notes': False,
'pos_iface_printbill': False,
'pos_iface_splitbill': False,
'pos_is_order_printer': False,
'pos_is_table_management': False,
})
else:
@@ -31,7 +28,6 @@ class ResConfigSettings(models.TransientModel):
'pos_iface_orderline_notes': res_config.pos_config_id.iface_orderline_notes,
'pos_iface_printbill': res_config.pos_config_id.iface_printbill,
'pos_iface_splitbill': res_config.pos_config_id.iface_splitbill,
'pos_is_order_printer': res_config.pos_config_id.is_order_printer,
'pos_is_table_management': res_config.pos_config_id.is_table_management,
})
@@ -1,6 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_restaurant_printer,restaurant.printer.user,model_restaurant_printer,point_of_sale.group_pos_user,1,0,0,0
access_restaurant_printer_manager,restaurant.printer.manager,model_restaurant_printer,point_of_sale.group_pos_manager,1,1,1,1
access_restaurant_floor,restaurant.floor.user,model_restaurant_floor,point_of_sale.group_pos_user,1,0,0,0
access_restaurant_floor_manager,restaurant.floor.manager,model_restaurant_floor,point_of_sale.group_pos_manager,1,1,1,1
access_restaurant_table,restaurant.table.user,model_restaurant_table,point_of_sale.group_pos_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
access_restaurant_printer restaurant.printer.user model_restaurant_printer point_of_sale.group_pos_user 1 0 0 0
access_restaurant_printer_manager restaurant.printer.manager model_restaurant_printer point_of_sale.group_pos_manager 1 1 1 1
2 access_restaurant_floor restaurant.floor.user model_restaurant_floor point_of_sale.group_pos_user 1 0 0 0
3 access_restaurant_floor_manager restaurant.floor.manager model_restaurant_floor point_of_sale.group_pos_manager 1 1 1 1
4 access_restaurant_table restaurant.table.user model_restaurant_table point_of_sale.group_pos_user 1 0 0 0
@@ -55,7 +55,6 @@ export class Table extends Component {
font-size: ${table.height >= 150 && table.width >= 150 ? 32 : 16}px;
`;
}
}
get fill() {
const customerCount = this.env.pos.getCustomerCount(this.props.table.id);
@@ -71,7 +70,7 @@ export class Table extends Component {
}
get orderCountClass() {
const countClass = { "order-count": true };
if (this.env.pos.config.iface_printers) {
if (this.env.pos.orderPreparationCategories.size) {
const notifications = this._getNotifications();
countClass["notify-printing"] = notifications.printing;
countClass["notify-skipped"] = notifications.skipped;
@@ -17,9 +17,6 @@ const NON_IDLE_EVENTS = [
];
let IDLE_TIMER_SETTER;
/**
* Go back to the floor screen if the user has been idle for a while.
*/
patch(PosStore.prototype, "pos_restaurant.PosStore", {
/**
* @override
@@ -11,7 +11,7 @@ patch(ActionpadWidget.prototype, "point_of_sale.ActionpadWidget", {
return (
this.props.actionName === "Payment" &&
this.pos.globalState.config.module_pos_restaurant &&
this.pos.globalState.printers_category_ids_set.size
this.pos.globalState.orderPreparationCategories.size
);
},
get currentOrder() {
@@ -32,7 +32,7 @@ patch(ActionpadWidget.prototype, "point_of_sale.ActionpadWidget", {
if (!this.clicked) {
this.clicked = true;
try {
this.currentOrder.submitOrder();
await this.pos.sendOrderInPreparation(this.currentOrder);
} finally {
this.clicked = false;
}
@@ -50,15 +50,18 @@ patch(ActionpadWidget.prototype, "point_of_sale.ActionpadWidget", {
get highlightPay() {
return (
this._super(...arguments) &&
this.pos.globalState.printers_category_ids_set.size &&
this.pos.globalState.orderPreparationCategories.size &&
!this.currentOrder.hasChangesToPrint() &&
this.hasQuantity(this.currentOrder)
);
},
get categoryCount() {
const categories = {};
for (const orderline of this.currentOrder.printingChanges.new) {
const category = this.pos.globalState.db.get_product_by_id(orderline.product_id).pos_categ_id[1];
const orderChange = this.currentOrder.getOrderChanges();
for (const idx in orderChange) {
const orderline = orderChange[idx];
const category = this.pos.globalState.db.get_product_by_id(orderline.product_id)
.pos_categ_id[1];
const numProd = orderline.quantity;
categories[category] = categories[category] ? categories[category] + numProd : numProd;
}
@@ -21,12 +21,12 @@ export class OrderlineNoteButton extends Component {
}
const { confirmed, payload: inputNote } = await this.popup.add(TextAreaPopup, {
startingValue: this.selectedOrderline.get_note(),
title: this.env._t("Add kitchen Note"),
startingValue: this.selectedOrderline.getNote(),
title: this.env._t("Add internal Note"),
});
if (confirmed) {
this.selectedOrderline.set_note(inputNote);
this.selectedOrderline.setNote(inputNote);
}
}
}
@@ -1,39 +0,0 @@
/** @odoo-module */
import { Orderline } from "@point_of_sale/js/Screens/ProductScreen/Orderline";
import { patch } from "@web/core/utils/patch";
patch(Orderline.prototype, "pos_restaurant.Orderline", {
/**
* @override
*/
get addedClasses() {
const res = this._super(...arguments);
Object.assign(res, {
dirty: this.props.line.mp_dirty,
skip: this.props.line.mp_skip,
});
return res;
},
/**
* @override
* if doubleclick, change mp_dirty to mp_skip
*
* IMPROVEMENT: Instead of handling both double click and click in single
* method, perhaps we can separate double click from single click.
*/
selectLine() {
const line = this.props.line; // the orderline
if (this.env.pos.get_order().selected_orderline.id !== line.id) {
this.mp_dbclk_time = new Date().getTime();
} else if (!this.mp_dbclk_time) {
this.mp_dbclk_time = new Date().getTime();
} else if (this.mp_dbclk_time + 500 > new Date().getTime()) {
line.set_skip(!line.mp_skip);
this.mp_dbclk_time = 0;
} else {
this.mp_dbclk_time = new Date().getTime();
}
this._super(...arguments);
},
});
@@ -65,9 +65,9 @@ export class SplitBillScreen extends Component {
// order and for the new one. This is not entirely correct
// but avoids flooding the kitchen with unnecessary orders.
// Not sure what to do in this case.
if (this.env.pos.isInterfacePrinter()) {
this.currentOrder.updatePrintedResume();
this.newOrder.updatePrintedResume();
if (this.env.pos.orderPreparationCategories.size) {
this.currentOrder.updateLastOrderChange();
this.newOrder.updateLastOrderChange();
}
this.newOrder.setCustomerCount(1);
+10 -314
View File
@@ -1,19 +1,13 @@
/** @odoo-module */
import { PosGlobalState, Order, Orderline, Payment } from "@point_of_sale/js/models";
import { uuidv4, batched, deduceUrl } from "@point_of_sale/js/utils";
import { HWPrinter } from "@point_of_sale/app/printer/hw_printer";
import { patch } from "@web/core/utils/patch";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { _t } from "@web/core/l10n/translation";
import { renderToElement } from "@web/core/utils/render";
patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
setup() {
this._super(...arguments);
this.orderToTransfer = null; // table transfer feature
this.transferredOrdersSet = new Set(); // used to know which orders has been transferred but not sent to the back end yet
this.printers_category_ids_set = new Set();
this.floorPlanStyle = "default";
this.isEditMode = false;
},
@@ -24,9 +18,6 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
this.floors = loadedData["restaurant.floor"];
this.loadRestaurantFloor();
}
if (this.config.module_pos_restaurant) {
this._loadRestaurantPrinter(loadedData["restaurant.printer"]);
}
},
//@override
async after_load_server_data() {
@@ -44,38 +35,11 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
this._super(...arguments);
}
},
isInterfacePrinter() {
return this.config.iface_printers;
},
addSubmitOrderButton() {
return this.config.module_pos_restaurant && this.unwatched.printers.length;
},
//@override
createReactiveOrder(json) {
let reactiveOrder = this._super(...arguments);
if (this.isInterfacePrinter()) {
const updateOrderChanges = () => {
if (reactiveOrder.get_screen_data().name === "ProductScreen") {
reactiveOrder.updateChangesToPrint();
}
};
reactiveOrder = owl.reactive(reactiveOrder, batched(updateOrderChanges));
reactiveOrder.updateChangesToPrint();
}
return reactiveOrder;
},
_loadRestaurantPrinter(printers) {
this.unwatched.printers = [];
// list of product categories that belong to one or more order printer
for (const printerConfig of printers) {
const printer = this.create_printer(printerConfig);
printer.config = printerConfig;
this.unwatched.printers.push(printer);
for (const id of printer.config.product_categories_ids) {
this.printers_category_ids_set.add(id);
}
}
this.config.iface_printers = !!this.unwatched.printers.length;
add_new_order() {
const order = this._super(...arguments);
this.ordersToUpdateSet.add(order);
return order;
},
async _getTableOrdersFromServer(tableIds) {
this.set_synch("connecting", 1);
@@ -98,25 +62,12 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
* @param tableId ID of the table we want to sync
*/
async _syncTableOrdersToServer() {
await this._pushOrdersToServer();
await this.sendDraftToServer();
await this._removeOrdersFromServer();
// This need to be called here otherwise _onReactiveOrderUpdated() will be called after the set is being cleared
this.ordersToUpdateSet.clear();
this.transferredOrdersSet.clear();
},
/**
* Send the orders to be saved to the back end
* @throw error
*/
async _pushOrdersToServer() {
const ordersUidsToSync = [...this.ordersToUpdateSet].map((order) => order.uid);
const ordersToSync = this.db.get_unpaid_orders_to_sync(ordersUidsToSync);
const ordersResponse = await this._save_to_server(ordersToSync, { draft: true });
const tableOrders = [...this.ordersToUpdateSet].map((order) => order);
ordersResponse.forEach((orderResponseData) =>
this._updateOrder(orderResponseData, tableOrders)
);
},
/**
* Replace all the orders of a table by orders fetched from the backend
* @param tableId ID of the table
@@ -230,10 +181,6 @@ patch(PosGlobalState.prototype, "pos_restaurant.PosGlobalState", {
const tableOrders = this.getTableOrders(tableId).filter((order) => !order.finalized);
return tableOrders.reduce((count, order) => count + order.getCustomerCount(), 0);
},
create_printer(config) {
const url = deduceUrl(config.proxy_ip || "");
return new HWPrinter({ rpc: this.env.services.rpc, url });
},
isOpenOrderShareable() {
return this._super(...arguments) || this.config.is_table_management;
},
@@ -257,16 +204,6 @@ patch(Order.prototype, "pos_restaurant.Order", {
}
this.customerCount = this.customerCount || 1;
}
if (this.pos.isInterfacePrinter()) {
// printedResume will store the previous state of the orderlines (when there were no skip), it will
// store all the orderlines even if the product are not printable. This way, when we add a new category in
// the printers, the already added products of the newly added category are not printed.
this.printedResume = owl.markRaw(this.printedResume || {}); // we don't wanna track it and re-render
// no need to store this in the backend, we can just compute it once the order is fetched from clicking a table
if (!this.printingChanges) {
this._resetPrintingChanges();
}
}
},
//@override
export_as_JSON() {
@@ -277,11 +214,7 @@ patch(Order.prototype, "pos_restaurant.Order", {
}
json.customer_count = this.customerCount;
}
if (this.pos.isInterfacePrinter()) {
json.multiprint_resume = JSON.stringify(this.printedResume);
// so that it can be stored in local storage and be used when loading the pos in the floorscreen
json.printing_changes = JSON.stringify(this.printingChanges);
}
return json;
},
//@override
@@ -294,24 +227,6 @@ patch(Order.prototype, "pos_restaurant.Order", {
}
this.customerCount = json.customer_count;
}
if (this.pos.isInterfacePrinter()) {
this.printedResume = json.multiprint_resume && JSON.parse(json.multiprint_resume);
this.printingChanges = json.printing_changes && JSON.parse(json.printing_changes);
}
},
async submitOrder() {
if (this.pos.unwatched.printers.length) {
if (this.hasChangesToPrint()) {
const isPrintSuccessful = await this.printChanges();
if (!isPrintSuccessful) {
this.pos.env.services.popup.add(ErrorPopup, {
title: _t("Printing failed"),
body: _t("Failed in printing the changes in the order"),
});
}
}
}
this.updatePrintedResume();
},
//@override
export_for_printing() {
@@ -324,79 +239,6 @@ patch(Order.prototype, "pos_restaurant.Order", {
}
return json;
},
_resetPrintingChanges() {
this.printingChanges = { new: [], cancelled: [] };
},
/**
* @returns {{ [productKey: string]: { product_id: number, name: string, note: string, quantity: number } }}
*/
_computePrintChanges() {
const changes = {};
// If there's a new orderline, we add it otherwise we add the change if there's one
this.orderlines.forEach((line) => {
if (!line.mp_skip) {
const productId = line.get_product().id;
const note = line.get_note();
const productKey = `${productId} - ${line.get_full_product_name()} - ${note}`;
const lineKey = `${line.uuid} - ${note}`;
const quantityDiff =
line.get_quantity() -
(this.printedResume[lineKey] ? this.printedResume[lineKey]["quantity"] : 0);
if (quantityDiff) {
if (!changes[productKey]) {
changes[productKey] = {
product_id: productId,
name: line.get_full_product_name(),
note: note,
quantity: quantityDiff,
};
} else {
changes[productKey]["quantity"] += quantityDiff;
}
line.set_dirty(true);
} else {
line.set_dirty(false);
}
}
});
// If there's an orderline that's not present anymore, we consider it as removed (even if note changed)
for (const [lineKey, lineResume] of Object.entries(this.printedResume)) {
if (!this._getPrintedLine(lineKey)) {
const productKey = `${lineResume["product_id"]} - ${lineResume["name"]} - ${lineResume["note"]}`;
if (!changes[productKey]) {
changes[productKey] = {
product_id: lineResume["product_id"],
name: lineResume["name"],
note: lineResume["note"],
quantity: -lineResume["quantity"],
};
} else {
changes[productKey]["quantity"] -= lineResume["quantity"];
}
}
}
return changes;
},
_getPrintingCategoriesChanges(categories) {
return {
new: this.printingChanges["new"].filter((change) =>
this.pos.db.is_product_in_category(categories, change["product_id"])
),
cancelled: this.printingChanges["cancelled"].filter((change) =>
this.pos.db.is_product_in_category(categories, change["product_id"])
),
};
},
_getPrintedLine(lineKey) {
return this.orderlines.find(
(line) =>
line.uuid === this.printedResume[lineKey]["line_uuid"] &&
line.note === this.printedResume[lineKey]["note"]
);
},
getCustomerCount() {
return this.customerCount;
},
@@ -409,130 +251,12 @@ patch(Order.prototype, "pos_restaurant.Order", {
}
return null;
},
updatePrintedResume() {
// we first remove the removed orderlines
for (const lineKey in this.printedResume) {
if (!this._getPrintedLine(lineKey)) {
delete this.printedResume[lineKey];
}
}
// we then update the added orderline or product quantity change
this.orderlines.forEach((line) => {
if (!line.mp_skip) {
const note = line.get_note();
const lineKey = `${line.uuid} - ${note}`;
if (this.printedResume[lineKey]) {
this.printedResume[lineKey]["quantity"] = line.get_quantity();
} else {
this.printedResume[lineKey] = {
line_uuid: line.uuid,
product_id: line.get_product().id,
name: line.get_full_product_name(),
note: note,
quantity: line.get_quantity(),
};
}
line.set_dirty(false);
}
});
this._resetPrintingChanges();
},
updateChangesToPrint() {
const changes = this._computePrintChanges(); // it's possible to have a change's quantity of 0
// we thoroughly parse the changes we just computed to properly separate them into two
const toAdd = [];
const toRemove = [];
for (const lineChange of Object.values(changes)) {
if (lineChange["quantity"] > 0) {
toAdd.push(lineChange);
} else if (lineChange["quantity"] < 0) {
lineChange["quantity"] *= -1; // we change the sign because that's how it is
toRemove.push(lineChange);
}
}
this.printingChanges = { new: toAdd, cancelled: toRemove };
},
hasChangesToPrint() {
for (const printer of this.pos.unwatched.printers) {
const changes = this._getPrintingCategoriesChanges(
printer.config.product_categories_ids
);
if (changes["new"].length > 0 || changes["cancelled"].length > 0) {
return true;
}
}
return false;
},
hasSkippedChanges() {
var orderlines = this.get_orderlines();
for (var i = 0; i < orderlines.length; i++) {
if (orderlines[i].mp_skip) {
return true;
}
}
return false;
},
async printChanges() {
let isPrintSuccessful = true;
const d = new Date();
let hours = "" + d.getHours();
hours = hours.length < 2 ? "0" + hours : hours;
let minutes = "" + d.getMinutes();
minutes = minutes.length < 2 ? "0" + minutes : minutes;
for (const printer of this.pos.unwatched.printers) {
const changes = this._getPrintingCategoriesChanges(
printer.config.product_categories_ids
);
if (changes["new"].length > 0 || changes["cancelled"].length > 0) {
const printingChanges = {
new: changes["new"],
cancelled: changes["cancelled"],
table_name: this.pos.config.is_table_management ? this.getTable().name : false,
floor_name: this.pos.config.is_table_management
? this.getTable().floor.name
: false,
name: this.name || "unknown order",
time: {
hours,
minutes,
},
};
const receipt = renderToElement("OrderChangeReceipt", { changes: printingChanges });
const result = await printer.printReceipt(receipt);
if (!result.successful) {
isPrintSuccessful = false;
}
}
}
return isPrintSuccessful;
},
});
patch(Orderline.prototype, "pos_restaurant.Orderline", {
setup() {
this._super(...arguments);
this.note = this.note || "";
if (this.pos.isInterfacePrinter()) {
this.uuid = this.uuid || uuidv4();
// mp dirty is true if this orderline has changed since the last kitchen print
this.mp_dirty = false;
if (!this.mp_skip) {
// mp_skip is true if the cashier want this orderline
// not to be sent to the kitchen
this.mp_skip = false;
}
}
},
//@override
can_be_merged_with(orderline) {
if (orderline.get_note() !== this.get_note()) {
return false;
} else {
return !this.mp_skip && !orderline.mp_skip && this._super(...arguments);
}
},
//@override
clone() {
@@ -544,9 +268,8 @@ patch(Orderline.prototype, "pos_restaurant.Orderline", {
export_as_JSON() {
const json = this._super(...arguments);
json.note = this.note;
if (this.pos.isInterfacePrinter()) {
if (this.pos.config.iface_printers) {
json.uuid = this.uuid;
json.mp_skip = this.mp_skip;
}
return json;
},
@@ -554,44 +277,17 @@ patch(Orderline.prototype, "pos_restaurant.Orderline", {
init_from_JSON(json) {
this._super(...arguments);
this.note = json.note;
if (this.pos.isInterfacePrinter()) {
if (this.pos.config.iface_printers) {
this.uuid = json.uuid;
this.mp_skip = json.mp_skip;
}
},
set_note(note) {
this.note = note;
},
get_note() {
return this.note;
},
set_skip(skip) {
if (this.mp_dirty && skip && !this.mp_skip) {
this.mp_skip = true;
}
if (this.mp_skip && !skip) {
this.mp_skip = false;
}
},
set_dirty(dirty) {
if (this.printable()) {
this.mp_dirty = dirty;
}
},
get_line_diff_hash() {
if (this.get_note()) {
return this.id + "|" + this.get_note();
if (this.getNote()) {
return this.id + "|" + this.getNote();
} else {
return "" + this.id;
}
},
// can this orderline be potentially printed ?
printable() {
return this.pos.db.is_product_in_category(
this.pos.printers_category_ids_set,
this.get_product().id
);
},
});
patch(Payment.prototype, "pos_restaurant.Payment", {
@@ -95,18 +95,6 @@
font-size: 24px;
line-height: 42px;
}
/* ------ ORDER LINE STATUS ------- */
.pos .order .orderline.dirty {
border-left: solid 6px #6EC89B;
color: #6EC89B;
padding-left: 9px;
}
.pos .order .orderline.skip {
border-left: solid 6px #7F82AC;
color: #7F82AC;
padding-left: 9px;
}
/* ------ SPLIT BILL SCREEN ------- */
@@ -5,7 +5,7 @@
<div class="control-button" t-on-click="() => this.click()">
<i class="fa fa-tag" />
<span> </span>
<span>Kitchen Note</span>
<span>Internal Note</span>
</div>
</t>
@@ -3,10 +3,10 @@
<t t-name="Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
<xpath expr="//ul[hasclass('info-list')]" position="inside">
<t t-if="props.line.get_note()">
<t t-if="props.line.getNote()">
<li class="info orderline-note">
<i class="fa fa-tag" role="img" aria-label="Note" title="Note"/>
<t t-esc="props.line.get_note()" />
<t t-esc="props.line.getNote()" />
</li>
</t>
</xpath>
@@ -24,7 +24,7 @@ class DoExt extends Do {
return [
{
content: "click note button",
trigger: '.control-buttons .control-button span:contains("Kitchen Note")',
trigger: '.control-buttons .control-button span:contains("Internal Note")',
},
];
}
@@ -15,10 +15,6 @@ class TestFrontend(odoo.tests.HttpCase):
'account_type': 'asset_receivable',
'reconcile': True})
printer = self.env['restaurant.printer'].create({
'name': 'Kitchen Printer',
'proxy_ip': 'localhost',
})
drinks_category = self.env['pos.category'].create({'name': 'Drinks'})
main_company = self.env.ref('base.main_company')
@@ -43,7 +39,6 @@ class TestFrontend(odoo.tests.HttpCase):
'iface_splitbill': True,
'iface_printbill': True,
'iface_orderline_notes': True,
'printer_ids': [(4, printer.id)],
'iface_start_categ_id': drinks_category.id,
'start_category': True,
})
@@ -111,61 +111,4 @@
action="action_restaurant_floor_form"
sequence="10"
groups="base.group_no_one"/>
<!-- RESTAURANT PRINTERS -->
<record id="view_restaurant_printer_form" model="ir.ui.view">
<field name="name">Order Printer</field>
<field name="model">restaurant.printer</field>
<field name="arch" type="xml">
<form string="POS Printer">
<sheet>
<group>
<field name="name" />
<field name="printer_type" widget="radio"/>
<field name="proxy_ip" attrs="{'invisible': [('printer_type', '!=', 'iot')]}"/>
<field name="product_categories_ids" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_restaurant_printer_form" model="ir.actions.act_window">
<field name="name">Order Printers</field>
<field name="res_model">restaurant.printer</field>
<field name="view_mode">tree,kanban,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new restaurant order printer
</p><p>
Order Printers are used by restaurants and bars to print the
order updates in the kitchen/bar when the waiter updates the order.
</p><p>
Each Order Printer has an IP Address that defines the IoT Box/Hardware
Proxy where the printer can be found, and a list of product categories.
An Order Printer will only print updates for products belonging to one of
its categories.
</p>
</field>
</record>
<record id="view_restaurant_printer" model="ir.ui.view">
<field name="name">Order Printers</field>
<field name="model">restaurant.printer</field>
<field name="arch" type="xml">
<tree string="Restaurant Order Printers">
<field name="name" />
<field name="proxy_ip" />
<field name="product_categories_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
<menuitem id="menu_restaurant_printer_all"
parent="point_of_sale.menu_point_config_product"
action="action_restaurant_printer_form"
sequence="15"
groups="base.group_no_one"/>
</odoo>
@@ -20,7 +20,7 @@
</div>
</div>
</setting>
<setting string="Kitchen Notes" help="Add kitchen notes on order lines for the kitchen" id="iface_orderline_notes" attrs="{'invisible': [('pos_module_pos_restaurant', '=', False)]}">
<setting string="Internal Notes" help="Add internal notes on order lines for the kitchen" id="iface_orderline_notes" attrs="{'invisible': [('pos_module_pos_restaurant', '=', False)]}">
<field name="pos_iface_orderline_notes"/>
</setting>
<setting string="Early Receipt Printing" help="Allow to print receipt before payment" id="iface_printbill" attrs="{'invisible': [('pos_module_pos_restaurant', '=', False)]}">
@@ -29,18 +29,6 @@
<setting help="Split total or order lines" id="iface_splitbill" attrs="{'invisible': [('pos_module_pos_restaurant', '=', False)]}">
<field name="pos_iface_splitbill" string="Allow Bill Splitting"/>
</setting>
<setting string="Kitchen Printers" help="Print orders at the kitchen, at the bar, etc." id="is_order_printer" attrs="{'invisible': [('pos_module_pos_restaurant', '=', False)]}">
<field name="pos_is_order_printer"/>
<div class="content-group" attrs="{'invisible': [('pos_is_order_printer', '=', False)]}">
<div class="mt16">
<label string="Printers" for="pos_printer_ids" class="o_light_label"/>
<field name="pos_printer_ids" widget="many2many_tags"/>
</div>
<div>
<button name="%(pos_restaurant.action_restaurant_printer_form)d" icon="fa-arrow-right" type="action" string="Printers" class="btn-link"/>
</div>
</div>
</setting>
</block>
</block>
<div id="tip_product" position="after">