diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 6dcf73892e5..12f9449c5f2 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -57,7 +57,8 @@ class AccountMove(models.Model):
total_amount += amount
for partial_line in (line.matched_debit_ids + line.matched_credit_ids):
total_reconciled += partial_line.amount
- if float_is_zero(total_amount, precision_rounding=move.currency_id.rounding):
+ precision_currency = move.currency_id or move.company_id.currency_id
+ if float_is_zero(total_amount, precision_rounding=precision_currency.rounding):
move.matched_percentage = 1.0
else:
move.matched_percentage = total_reconciled / total_amount
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index aeff7ce574a..bbf1177a02d 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -151,11 +151,14 @@ class Warehouse(models.Model):
# If another partner assigned
if vals.get('partner_id'):
warehouses._update_partner_data(vals['partner_id'], vals.get('company_id'))
+
res = super(Warehouse, self).write(vals)
# check if we need to delete and recreate route
if vals.get('reception_steps') or vals.get('delivery_steps'):
- warehouses._update_routes()
+ route_vals = warehouses._update_routes()
+ if route_vals:
+ self.write(route_vals)
if vals.get('resupply_wh_ids') and not vals.get('resupply_route_ids'):
for warehouse in warehouses:
@@ -560,12 +563,19 @@ class Warehouse(models.Model):
# change the default source and destination location and (de)activate picking types
self._update_picking_type()
# update delivery route and rules: unlink the existing rules of the warehouse delivery route and recreate it
- self._create_or_update_delivery_route(routes_data)
+ delivery_route = self._create_or_update_delivery_route(routes_data)
# update receipt route and rules: unlink the existing rules of the warehouse receipt route and recreate it
- self._create_or_update_reception_route(routes_data)
- self._create_or_update_crossdock_route(routes_data)
- self._create_or_update_mto_pull(routes_data)
- return True
+ reception_route = self._create_or_update_reception_route(routes_data)
+ crossdock_route = self._create_or_update_crossdock_route(routes_data)
+ mto_pull = self._create_or_update_mto_pull(routes_data)
+
+ return {
+ 'route_ids': [(4, route.id) for route in reception_route | delivery_route | crossdock_route],
+ 'mto_pull_id': mto_pull.id,
+ 'reception_route_id': reception_route.id,
+ 'delivery_route_id': delivery_route.id,
+ 'crossdock_route_id': crossdock_route.id,
+ }
@api.one
def _update_picking_type(self):
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index 3c80e5c08e3..06f1ee0a4cb 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -1258,9 +1258,40 @@ var FieldBinaryFile = FieldBinary.extend({
});
}
},
+ /**
+ * Render the value of the binary file field
+ *
+ * The value depends on the mode (readonly/edit) and the attribute filename
+ * in the xml node of the field:
+ *
+ *
+ * with filename without filename
+ * [------------------|-----------------------]
+ * readonly: | saved filename | binary size |
+ * edit: | current filename | base64 representation |
+ * [------------------|-----------------------]
+ *
+ *
+ * This is how the filename is retrieved:
+ *
+ * - Suppose that the binary field is named 'data'
+ * - The xml node of this field is as follow:
+ *
+ * ``
+ *
+ * - 'fdata' is another field whose value is the filename
+ * - On the following record:
+ *
+ * `{data: "Cg==1das02fa01", fdata: 'my-file.txt'}`
+ *
+ * - The content of the file is the value of 'data': Cg==1das02fa01
+ * - The filename is the value of 'fdata': my-file.txt
+ */
render_value: function() {
- var filename = this.view.datarecord[this.node.attrs.filename];
+ var filename;
if (this.get("effective_readonly")) {
+ // Filename from saved state (might render from a discard operation)
+ filename = this.view.datarecord[this.node.attrs.filename]; // do not forward-port >= 11.0
this.do_toggle(!!this.get('value'));
if (this.get('value')) {
this.$el.empty().append($("").addClass('fa fa-download'));
@@ -1274,6 +1305,9 @@ var FieldBinaryFile = FieldBinary.extend({
}
}
} else {
+ // Filename at the moment (might be unsaved state)
+ var filenameField = this.field_manager.fields[this.node.attrs.filename]; // do not forward-port >= 11.0
+ filename = filenameField ? filenameField.get('value') : '';
if(this.get('value')) {
this.$el.children().removeClass('o_hidden');
this.$('.o_select_file_button').first().addClass('o_hidden');
diff --git a/odoo/netsvc.py b/odoo/netsvc.py
index ee961529c70..0c64ebe51aa 100644
--- a/odoo/netsvc.py
+++ b/odoo/netsvc.py
@@ -139,6 +139,13 @@ def init_logger():
if dirname and not os.path.isdir(dirname):
os.makedirs(dirname)
if tools.config['logrotate'] is not False:
+ if tools.config['workers'] > 1:
+ # TODO: fallback to regular file logging in master for safe(r) defaults?
+ #
+ # Doing so here would be a good idea but also might break
+ # situations were people do log-shipping of rotated data?
+ _logger.warn("WARNING: built-in log rotation is not reliable in multi-worker scenarios and may incur significant data loss. "
+ "It is strongly recommended to use an external log rotation utility or use system loggers (--syslog) instead.")
handler = logging.handlers.TimedRotatingFileHandler(filename=logf, when='D', interval=1, backupCount=30)
elif os.name == 'posix':
handler = logging.handlers.WatchedFileHandler(logf)