[MERGE] forward port branch 11.0 up to 22a13073f0
This commit is contained in:
@@ -227,7 +227,7 @@ class AccountInvoice(models.Model):
|
||||
for line in self.move_id.line_ids.filtered(lambda l: l.account_id.id == self.account_id.id):
|
||||
payment_lines.update(line.mapped('matched_credit_ids.credit_move_id.id'))
|
||||
payment_lines.update(line.mapped('matched_debit_ids.debit_move_id.id'))
|
||||
self.payment_move_line_ids = self.env['account.move.line'].browse(list(payment_lines))
|
||||
self.payment_move_line_ids = self.env['account.move.line'].browse(list(payment_lines)).sorted()
|
||||
|
||||
name = fields.Char(string='Reference/Description', index=True,
|
||||
readonly=True, states={'draft': [('readonly', False)]}, copy=False, help='The name that will be used on account move lines')
|
||||
|
||||
@@ -115,6 +115,8 @@ class MailMail(models.Model):
|
||||
ids = filtered_ids
|
||||
else:
|
||||
ids = list(set(filtered_ids) & set(ids))
|
||||
ids.sort()
|
||||
|
||||
res = None
|
||||
try:
|
||||
# auto-commit except in testing mode
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_mode">kanban,tree,form,calendar,pivot,graph</field>
|
||||
<field name="context">{
|
||||
'group_by': 'stage_id',
|
||||
'default_group_by': 'stage_id',
|
||||
'search_default_project_id': [active_id],
|
||||
'default_project_id': active_id,
|
||||
}</field>
|
||||
|
||||
@@ -510,6 +510,8 @@ class StockMoveLine(models.Model):
|
||||
candidate.product_uom_qty = 0.0
|
||||
else:
|
||||
candidate.unlink()
|
||||
if float_is_zero(quantity, precision_rounding=rounding):
|
||||
break
|
||||
else:
|
||||
# split this move line and assign the new part to our extra move
|
||||
quantity_split = float_round(
|
||||
@@ -517,8 +519,6 @@ class StockMoveLine(models.Model):
|
||||
precision_rounding=self.product_uom_id.rounding,
|
||||
rounding_method='UP')
|
||||
candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(quantity_split, candidate.product_uom_id, rounding_method='HALF-UP')
|
||||
quantity -= quantity_split
|
||||
move_to_recompute_state |= candidate.move_id
|
||||
if quantity == 0.0:
|
||||
break
|
||||
move_to_recompute_state._recompute_state()
|
||||
|
||||
@@ -2004,6 +2004,48 @@ class StockMove(TransactionCase):
|
||||
self.assertAlmostEqual(move_pack_cust.reserved_availability, 1.0)
|
||||
self.assertEqual(move_pack_cust.state, 'partially_available')
|
||||
|
||||
def test_use_reserved_move_line_1(self):
|
||||
""" Test that _free_reservation work when quantity is only available on
|
||||
reserved move lines.
|
||||
"""
|
||||
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 10.0)
|
||||
move1 = self.env['stock.move'].create({
|
||||
'name': 'test_use_unreserved_move_line_1_1',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 5.0,
|
||||
})
|
||||
move2 = self.env['stock.move'].create({
|
||||
'name': 'test_use_unreserved_move_line_1_1',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 5.0,
|
||||
})
|
||||
move1._action_confirm()
|
||||
move1._action_assign()
|
||||
move2._action_confirm()
|
||||
move2._action_assign()
|
||||
move3 = self.env['stock.move'].create({
|
||||
'name': 'test_use_unreserved_move_line_1_1',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 0.0,
|
||||
'quantity_done': 1.0,
|
||||
})
|
||||
move3._action_confirm()
|
||||
move3._action_assign()
|
||||
move3._action_done()
|
||||
self.assertEqual(move3.state, 'done')
|
||||
quant = self.env['stock.quant']._gather(self.product1, self.stock_location)
|
||||
self.assertEqual(quant.quantity, 9.0)
|
||||
self.assertEqual(quant.reserved_quantity, 9.0)
|
||||
|
||||
def test_use_unreserved_move_line_1(self):
|
||||
""" Test that validating a stock move linked to an untracked product reserved by another one
|
||||
correctly unreserves the other one.
|
||||
|
||||
@@ -273,3 +273,35 @@ var AbstractView = Class.extend({
|
||||
return AbstractView;
|
||||
|
||||
});
|
||||
|
||||
odoo.define('web.viewUtils', function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* FIXME: move this module to its own file in master
|
||||
*/
|
||||
|
||||
var utils = {
|
||||
/**
|
||||
* States whether or not the quick create feature is available for the given
|
||||
* datapoint, depending on its groupBy field.
|
||||
*
|
||||
* @param {Object} list dataPoint of type list
|
||||
* @returns {Boolean} true iff the kanban quick create feature is available
|
||||
*/
|
||||
isQuickCreateEnabled: function (list) {
|
||||
var groupByField = list.groupedBy[0] && list.groupedBy[0].split(':')[0];
|
||||
if (!groupByField) {
|
||||
return false;
|
||||
}
|
||||
var availableTypes = ['char', 'boolean', 'many2one'];
|
||||
if (!_.contains(availableTypes, list.fields[groupByField].type)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
return utils;
|
||||
|
||||
});
|
||||
|
||||
@@ -213,7 +213,7 @@ var BasicView = AbstractView.extend({
|
||||
attrs.options = attrs.options ? pyeval.py_eval(attrs.options) : {};
|
||||
}
|
||||
|
||||
if (attrs.on_change && !field.onChange) {
|
||||
if (attrs.on_change && attrs.on_change !== "0" && !field.onChange) {
|
||||
field.onChange = "1";
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ var Context = require('web.Context');
|
||||
var core = require('web.core');
|
||||
var Domain = require('web.Domain');
|
||||
var view_dialogs = require('web.view_dialogs');
|
||||
var viewUtils = require('web.viewUtils');
|
||||
|
||||
var _t = core._t;
|
||||
var qweb = core.qweb;
|
||||
@@ -34,12 +35,15 @@ var KanbanController = BasicController.extend({
|
||||
/**
|
||||
* @override
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.quickCreateEnabled set to false to disable the
|
||||
* quick create feature
|
||||
*/
|
||||
init: function (parent, model, renderer, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.on_create = params.on_create;
|
||||
this.hasButtons = params.hasButtons;
|
||||
this.quickCreateEnabled = params.quickCreateEnabled;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -259,8 +263,8 @@ var KanbanController = BasicController.extend({
|
||||
*/
|
||||
_onButtonNew: function () {
|
||||
var state = this.model.get(this.handle, {raw: true});
|
||||
var hasColumns = state.groupedBy.length > 0 && state.data.length > 0;
|
||||
if (hasColumns && this.on_create === 'quick_create') {
|
||||
var quickCreateEnabled = this.quickCreateEnabled && viewUtils.isQuickCreateEnabled(state);
|
||||
if (this.on_create === 'quick_create' && quickCreateEnabled) {
|
||||
// Activate the quick create in the first column
|
||||
this.renderer.addQuickCreate();
|
||||
} else if (this.on_create && this.on_create !== 'quick_create') {
|
||||
|
||||
@@ -9,6 +9,7 @@ var ColumnQuickCreate = require('web.kanban_column_quick_create');
|
||||
var QWeb = require('web.QWeb');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
var viewUtils = require('web.viewUtils');
|
||||
|
||||
var qweb = core.qweb;
|
||||
|
||||
@@ -93,6 +94,9 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
}),
|
||||
/**
|
||||
* @override
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.quickCreateEnabled set to false to disable the
|
||||
* quick create feature
|
||||
*/
|
||||
init: function (parent, state, params) {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -111,6 +115,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
if (this.columnOptions.hasProgressBar) {
|
||||
this.columnOptions.progressBarStates = {};
|
||||
}
|
||||
this.quickCreateEnabled = params.quickCreateEnabled;
|
||||
this._setState(state);
|
||||
},
|
||||
/**
|
||||
@@ -423,6 +428,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
groupedBy: groupByField,
|
||||
grouped_by_m2o: this.groupedByM2O,
|
||||
relation: relation,
|
||||
quick_create: this.quickCreateEnabled && viewUtils.isQuickCreateEnabled(state),
|
||||
});
|
||||
this.createColumnEnabled = this.groupedByM2O && this.columnOptions.group_creatable;
|
||||
},
|
||||
|
||||
@@ -62,7 +62,6 @@ var KanbanView = BasicView.extend({
|
||||
editable: activeActions.group_edit,
|
||||
deletable: activeActions.group_delete,
|
||||
group_creatable: activeActions.group_create && !config.device.isMobile,
|
||||
quick_create: params.isQuickCreateEnabled || this._isQuickCreateEnabled(),
|
||||
quickCreateView: this.arch.attrs.quick_create_view || null,
|
||||
hasProgressBar: !!progressBar,
|
||||
};
|
||||
@@ -71,6 +70,7 @@ var KanbanView = BasicView.extend({
|
||||
deletable: activeActions.delete,
|
||||
read_only_mode: params.readOnlyMode,
|
||||
};
|
||||
this.rendererParams.quickCreateEnabled = this._isQuickCreateEnabled();
|
||||
var examples = this.arch.attrs.examples;
|
||||
if (examples) {
|
||||
this.rendererParams.examples = kanbanExamplesRegistry.get(examples);
|
||||
@@ -79,6 +79,7 @@ var KanbanView = BasicView.extend({
|
||||
this.controllerParams.on_create = this.arch.attrs.on_create;
|
||||
this.controllerParams.readOnlyMode = false;
|
||||
this.controllerParams.hasButtons = true;
|
||||
this.controllerParams.quickCreateEnabled = this.rendererParams.quickCreateEnabled;
|
||||
|
||||
if (config.device.isMobile) {
|
||||
this.jsLibs.push('/web/static/lib/jquery.touchSwipe/jquery.touchSwipe.js');
|
||||
@@ -92,13 +93,10 @@ var KanbanView = BasicView.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} viewInfo
|
||||
* @returns {boolean} true iff the quick create feature is not explicitely
|
||||
* disabled (with create="False" or quick_create="False" in the arch)
|
||||
*/
|
||||
_isQuickCreateEnabled: function () {
|
||||
var groupBy = this.loadParams.groupBy[0];
|
||||
groupBy = groupBy !== undefined ? groupBy.split(':')[0] : undefined;
|
||||
if (groupBy !== undefined && !_.contains(['char', 'boolean', 'many2one'], this.fields[groupBy].type)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.controllerParams.activeActions.create) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -790,7 +790,7 @@
|
||||
</t>
|
||||
<t t-name="FieldMany2One">
|
||||
<t t-if="widget.mode === 'readonly'">
|
||||
<a t-if="!widget.nodeOptions.no_open" class="o_form_uri" href="#"/>
|
||||
<a t-if="!widget.nodeOptions.no_open" t-att-tabindex="widget.attrs.tabindex" class="o_form_uri" href="#"/>
|
||||
<span t-if="widget.nodeOptions.no_open"/>
|
||||
</t>
|
||||
<div t-if="widget.mode === 'edit'" class="o_field_widget o_field_many2one">
|
||||
|
||||
@@ -1218,6 +1218,81 @@ QUnit.module('Views', {
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('quick create record in grouped on date(time) field', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var kanban = createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<kanban class="o_kanban_test" on_create="quick_create">' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="display_name"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['date'],
|
||||
intercepts: {
|
||||
switch_view: function (ev) {
|
||||
assert.deepEqual(_.pick(ev.data, 'res_id', 'view_type'), {
|
||||
res_id: undefined,
|
||||
view_type: 'form',
|
||||
}, "should trigger an event to open the form view (twice)");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_header .o_kanban_quick_add i').length, 0,
|
||||
"quick create should be disabled when grouped on a date field");
|
||||
|
||||
// clicking on CREATE in control panel should not open a quick create
|
||||
kanban.$buttons.find('.o-kanban-button-new').click();
|
||||
assert.strictEqual(kanban.$('.o_kanban_quick_create').length, 0,
|
||||
"should not have opened the quick create widget");
|
||||
|
||||
kanban.reload({groupBy: ['datetime']});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_header .o_kanban_quick_add i').length, 0,
|
||||
"quick create should be disabled when grouped on a datetime field");
|
||||
|
||||
// clicking on CREATE in control panel should not open a quick create
|
||||
kanban.$buttons.find('.o-kanban-button-new').click();
|
||||
assert.strictEqual(kanban.$('.o_kanban_quick_create').length, 0,
|
||||
"should not have opened the quick create widget");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('quick create record feature is properly enabled/disabled at reload', function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
var kanban = createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<kanban class="o_kanban_test" on_create="quick_create">' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="display_name"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['foo'],
|
||||
});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_header .o_kanban_quick_add i').length, 3,
|
||||
"quick create should be enabled when grouped on a char field");
|
||||
|
||||
kanban.reload({groupBy: ['date']});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_header .o_kanban_quick_add i').length, 0,
|
||||
"quick create should now be disabled (grouped on date field)");
|
||||
|
||||
kanban.reload({groupBy: ['bar']});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_header .o_kanban_quick_add i').length, 2,
|
||||
"quick create should be enabled again (grouped on boolean field)");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('many2many_tags in kanban views', function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
@@ -3568,7 +3643,7 @@ QUnit.module('Views', {
|
||||
'<div><field name="foo"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['int_field'],
|
||||
groupBy: ['foo'],
|
||||
mockRPC: function (route, args) {
|
||||
if (route === '/web/dataset/resequence') {
|
||||
return $.when(true);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Germany, 2017-11-24
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Andreas Raster lazor@affenbande.org https://github.com/rakete
|
||||
@@ -0,0 +1,11 @@
|
||||
FRANCE, 2018-09-26
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
zoggy 43614933+zoggy444@users.noreply.github.com https://github.com/zoggy444
|
||||
@@ -852,7 +852,7 @@ attributes:
|
||||
``quick_create``
|
||||
whether it should be possible to create records without switching to the
|
||||
form view. By default, ``quick_create`` is enabled when the Kanban view is
|
||||
grouped, and disabled when not.
|
||||
grouped by many2one, char or boolean fields, and disabled when not.
|
||||
|
||||
Set to ``true`` to always enable it, and to ``false`` to always disable it.
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ CREATE TABLE res_company (
|
||||
name varchar NOT NULL,
|
||||
partner_id integer,
|
||||
currency_id integer,
|
||||
sequence integer,
|
||||
create_date timestamp without time zone,
|
||||
primary key(id)
|
||||
);
|
||||
|
||||
@@ -339,7 +339,7 @@ class IrActionsServer(models.Model):
|
||||
DEFAULT_PYTHON_CODE = """# Available variables:
|
||||
# - env: Odoo Environment on which the action is triggered
|
||||
# - model: Odoo Model of the record on which the action is triggered; is a void recordset
|
||||
# - record: record on which the action is triggered; may be be void
|
||||
# - record: record on which the action is triggered; may be void
|
||||
# - records: recordset of all records on which the action is triggered in multi-mode; may be void
|
||||
# - time, datetime, dateutil, timezone: useful Python libraries
|
||||
# - log: log(message, level='info'): logging function to record debug information in ir.logging table
|
||||
|
||||
@@ -768,6 +768,7 @@ class IrModelFields(models.Model):
|
||||
'index': bool(field.index),
|
||||
'store': bool(field.store),
|
||||
'copy': bool(field.copy),
|
||||
'on_delete': getattr(field, 'ondelete', None),
|
||||
'related': ".".join(field.related) if field.related else None,
|
||||
'readonly': bool(field.readonly),
|
||||
'required': bool(field.required),
|
||||
|
||||
@@ -135,6 +135,8 @@ class FSWatcher(object):
|
||||
try:
|
||||
source = open(path, 'rb').read() + b'\n'
|
||||
compile(source, path, 'exec')
|
||||
except FileNotFoundError:
|
||||
_logger.error('autoreload: python code change detected, FileNotFound for %s', path)
|
||||
except SyntaxError:
|
||||
_logger.error('autoreload: python code change detected, SyntaxError in %s', path)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user