diff --git a/addons/analytic/models/analytic_account.py b/addons/analytic/models/analytic_account.py index 916d4878144..de3bc507049 100644 --- a/addons/analytic/models/analytic_account.py +++ b/addons/analytic/models/analytic_account.py @@ -20,21 +20,24 @@ class AccountAnalyticAccount(models.Model): @api.multi def _compute_debit_credit_balance(self): analytic_line_obj = self.env['account.analytic.line'] - domain = [('account_id', 'in', self.mapped('id'))] + domain = [('account_id', 'in', self.ids)] if self._context.get('from_date', False): domain.append(('date', '>=', self._context['from_date'])) if self._context.get('to_date', False): domain.append(('date', '<=', self._context['to_date'])) - account_amounts = analytic_line_obj.search_read(domain, ['account_id', 'amount']) - account_ids = set([line['account_id'][0] for line in account_amounts]) - data_debit = {account_id: 0.0 for account_id in account_ids} - data_credit = {account_id: 0.0 for account_id in account_ids} - for account_amount in account_amounts: - if account_amount['amount'] < 0.0: - data_debit[account_amount['account_id'][0]] += account_amount['amount'] - else: - data_credit[account_amount['account_id'][0]] += account_amount['amount'] + credit_groups = analytic_line_obj.read_group( + domain=domain + [('amount', '>=', 0.0)], + fields=['account_id', 'amount'], + groupby=['account_id'] + ) + data_credit = {l['account_id'][0]: l['amount'] for l in credit_groups} + debit_groups = analytic_line_obj.read_group( + domain=domain + [('amount', '<', 0.0)], + fields=['account_id', 'amount'], + groupby=['account_id'] + ) + data_debit = {l['account_id'][0]: l['amount'] for l in debit_groups} for account in self: account.debit = abs(data_debit.get(account.id, 0.0)) diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index 3b8ed12851f..42c21c4f675 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -289,7 +289,9 @@ class AccountFrFec(models.TransientModel): END AS PieceRef, TO_CHAR(am.date, 'YYYYMMDD') AS PieceDate, - CASE WHEN aml.name IS NULL THEN '/' ELSE replace(aml.name, '|', '/') END AS EcritureLib, + CASE WHEN aml.name IS NULL THEN '/' + WHEN aml.name SIMILAR TO '[\t|\s|\n]*' THEN '/' + ELSE replace(aml.name, '|', '/') END AS EcritureLib, replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '000000000000000D99') END, '.', ',') AS Debit, replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '000000000000000D99') END, '.', ',') AS Credit, CASE WHEN rec.name IS NULL THEN '' ELSE rec.name END AS EcritureLet, diff --git a/addons/mrp/i18n/mrp.pot b/addons/mrp/i18n/mrp.pot index 793134c265b..804529224c9 100644 --- a/addons/mrp/i18n/mrp.pot +++ b/addons/mrp/i18n/mrp.pot @@ -2063,6 +2063,11 @@ msgstr "" msgid "Quantity To Produce" msgstr "" +#. module: mrp +#: model:ir.model.fields,field_description:mrp.field_stock_move_quantity_done_store +msgid "Quantity done store" +msgstr "" + #. module: mrp #: model:ir.model.fields,field_description:mrp.field_mrp_routing_workcenter_batch_size msgid "Quantity to Process" diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 32661b0351a..0c6f50cfc52 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -91,7 +91,7 @@ class StockMove(models.Model): quantity_available = fields.Float( 'Quantity Available', compute="_qty_available", digits=dp.get_precision('Product Unit of Measure')) - quantity_done_store = fields.Float('Quantity', digits=0) + quantity_done_store = fields.Float('Quantity done store', digits=0) quantity_done = fields.Float( 'Quantity', compute='_qty_done_compute', inverse='_qty_done_set', digits=dp.get_precision('Product Unit of Measure')) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 37c115973c4..7cbef6c99dc 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -255,6 +255,7 @@ exports.PosModel = Backbone.Model.extend({ // we attribute a role to the user, 'cashier' or 'manager', depending // on the group the user belongs. var pos_users = []; + var current_cashier = self.get_cashier(); for (var i = 0; i < users.length; i++) { var user = users[i]; for (var j = 0; j < user.groups_id.length; j++) { @@ -272,6 +273,8 @@ exports.PosModel = Backbone.Model.extend({ // replace the current user with its updated version if (user.id === self.user.id) { self.user = user; + } + if (user.id === current_cashier.id) { self.set_cashier(user); } } diff --git a/addons/web/static/src/js/framework/ajax.js b/addons/web/static/src/js/framework/ajax.js index e74ae67dc04..d6241974d79 100644 --- a/addons/web/static/src/js/framework/ajax.js +++ b/addons/web/static/src/js/framework/ajax.js @@ -252,12 +252,20 @@ function get_file(options) { if (options.error) { var body = this.contentDocument.body; var nodes = body.children.length === 0 ? body.childNodes : body.children; - options.error({ - message: nodes.length > 1 ? nodes[1].textContent : '', - data: { - title: nodes.length > 0 ? nodes[0].textContent : '', - }, - }); + var errorParams = {}; + + try { // Case of a serialized Odoo Exception: It is Json Parsable + var node = nodes[1] || nodes[0]; + errorParams = JSON.parse(node.textContent); + } catch (e) { // Arbitrary uncaught python side exception + errorParams = { + message: nodes.length > 1 ? nodes[1].textContent : '', + data: { + title: nodes.length > 0 ? nodes[0].textContent : '', + } + } + } + options.error(errorParams); } } finally { complete(); diff --git a/odoo/http.py b/odoo/http.py index a9e9e8cf9ed..84298c20a63 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -163,6 +163,7 @@ def redirect_with_hash(url, code=303): # See extensive test page at http://greenbytes.de/tech/tc/httpredirects/ if request.httprequest.user_agent.browser in ('firefox',): return werkzeug.utils.redirect(url, code) + url = url.strip() if urlparse.urlparse(url, scheme='http').scheme not in ('http', 'https'): url = 'http://' + url url = url.replace("'", "%27").replace("<", "%3C") diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index ffa3c58d52a..7428e203c5a 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -130,7 +130,7 @@ class Registry(Mapping): # Indicates that the registry is self.ready = False - # Inter-process signaling (used only when odoo.multi_process is True): + # Inter-process signaling: # The `base_registry_signaling` sequence indicates the whole registry # must be reloaded. # The `base_cache_signaling sequence` indicates all caches must be @@ -360,7 +360,7 @@ class Registry(Mapping): def setup_signaling(self): """ Setup the inter-process signaling on this registry. """ - if not odoo.multi_process: + if self.in_test_mode(): return with self.cursor() as cr: @@ -386,7 +386,7 @@ class Registry(Mapping): """ Check whether the registry has changed, and performs all necessary operations to update the registry. Return an up-to-date registry. """ - if not odoo.multi_process: + if self.in_test_mode(): return self with closing(self.cursor()) as cr: @@ -412,7 +412,7 @@ class Registry(Mapping): def signal_registry_change(self): """ Notifies other processes that the registry has changed. """ - if odoo.multi_process: + if not self.in_test_mode(): _logger.info("Registry changed, signaling through the database") with closing(self.cursor()) as cr: cr.execute("select nextval('base_registry_signaling')") @@ -420,7 +420,7 @@ class Registry(Mapping): def signal_caches_change(self): """ Notifies other processes if caches have been invalidated. """ - if odoo.multi_process and self.cache_cleared: + if self.cache_cleared and not self.in_test_mode(): # signal it through the database to other processes _logger.info("At least one model cache has been invalidated, signaling through the database.") with closing(self.cursor()) as cr: diff --git a/odoo/service/server.py b/odoo/service/server.py index 40d324a30c5..9273b84027e 100644 --- a/odoo/service/server.py +++ b/odoo/service/server.py @@ -92,9 +92,6 @@ class RequestHandler(werkzeug.serving.WSGIRequestHandler): me = threading.currentThread() me.name = 'odoo.service.http.request.%s' % (me.ident,) -# _reexec() should set LISTEN_* to avoid connection refused during reload time. It -# should also work with systemd socket activation. This is currently untested -# and not yet used. class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving.ThreadedWSGIServer): """ werkzeug Threaded WSGI Server patched to allow reusing a listen socket @@ -106,14 +103,15 @@ class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving. handler=RequestHandler) def server_bind(self): - envfd = os.environ.get('LISTEN_FDS') - if envfd and os.environ.get('LISTEN_PID') == str(os.getpid()): + SD_LISTEN_FDS_START = 3 + if os.environ.get('LISTEN_FDS') == '1' and os.environ.get('LISTEN_PID') == str(os.getpid()): self.reload_socket = True - self.socket = socket.fromfd(int(envfd), socket.AF_INET, socket.SOCK_STREAM) - # should we os.close(int(envfd)) ? it seem python duplicate the fd. + self.socket = socket.fromfd(SD_LISTEN_FDS_START, socket.AF_INET, socket.SOCK_STREAM) + _logger.info('HTTP service (werkzeug) running through socket activation') else: self.reload_socket = False super(ThreadedWSGIServerReloadable, self).server_bind() + _logger.info('HTTP service (werkzeug) running on %s:%s', self.server_name, self.server_port) def server_activate(self): if not self.reload_socket: @@ -252,7 +250,6 @@ class ThreadedServer(CommonServer): t = threading.Thread(target=self.http_thread, name="odoo.service.httpd") t.setDaemon(True) t.start() - _logger.info('HTTP service (werkzeug) running on %s:%s', self.interface, self.port) def start(self, stop=False): _logger.debug("Setting signal handlers") @@ -868,7 +865,8 @@ def _reexec(updated_modules=None): args += ["-u", ','.join(updated_modules)] if not args or args[0] != exe: args.insert(0, exe) - os.execv(sys.executable, args) + # We should keep the LISTEN_* environment variabled in order to support socket activation on reexec + os.execve(sys.executable, args, os.environ) def load_test_file_yml(registry, test_file): with registry.cursor() as cr: