[MERGE] Forwardport of saas-12.2 to saas-12.3 up to 01365a3f62

closes odoo/odoo#34747

Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
Martin Trigaux
2019-07-12 07:22:41 +00:00
29 changed files with 180 additions and 92 deletions
+3 -3
View File
@@ -1366,7 +1366,7 @@ class AccountTax(models.Model):
base = recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount)
incl_fixed_amount = incl_percent_amount = incl_division_amount = 0
store_included_tax_total = True
if tax.price_include:
if tax.price_include or self._context.get('force_price_include'):
if tax.amount_type == 'percent':
incl_percent_amount += tax.amount
elif tax.amount_type == 'division':
@@ -1395,7 +1395,7 @@ class AccountTax(models.Model):
cumulated_tax_included_amount = 0
for tax in taxes:
#compute the tax_amount
if tax.price_include and total_included_checkpoints.get(i):
if (self._context.get('force_price_include') or tax.price_include) and total_included_checkpoints.get(i):
# We know the total to reach for that tax, so we make a substraction to avoid any rounding issues
tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount)
cumulated_tax_included_amount = 0
@@ -1434,7 +1434,7 @@ class AccountTax(models.Model):
'sequence': tax.sequence,
'account_id': tax.cash_basis_transition_account_id.id if tax.tax_exigibility == 'on_payment' else repartition_line.account_id.id,
'analytic': tax.analytic,
'price_include': tax.price_include,
'price_include': tax.price_include or self._context.get('force_price_include'),
'tax_exigibility': tax.tax_exigibility,
'tax_repartition_line_id': repartition_line.id,
'tag_ids': [(6, False, (repartition_line.tag_ids + subsequent_tags).ids)],
+1 -1
View File
@@ -440,7 +440,7 @@ class AccountInvoice(models.Model):
vendor_display_name = _('From: ') + invoice.source_email
invoice.invoice_icon = '@'
else:
vendor_display_name = ('Created by: ') + invoice.create_uid.name
vendor_display_name = ('Created by: ') + invoice.sudo().create_uid.name
invoice.invoice_icon = '#'
invoice.vendor_display_name = vendor_display_name
@@ -102,7 +102,7 @@ class AccountReconcileModel(models.Model):
second_analytic_account_id = fields.Many2one('account.analytic.account', string='Second Analytic Account', ondelete='set null')
second_analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Second Analytic Tags',
relation='account_reconcile_model_second_analytic_tag_rel')
number_entries = fields.Integer(string='Number of entries related to this model', compute='_compute_number_entries')
@api.multi
@@ -113,7 +113,7 @@ class AccountReconcileModel(models.Model):
'help': """<p class="o_view_nocontent_empty_folder">{}</p>""".format(_('No move from this reconciliation model')),
})
return action
@api.multi
def _compute_number_entries(self):
data = self.env['account.move.line'].read_group([('reconcile_model_id', 'in', self.ids)], ['reconcile_model_ids'], 'reconcile_model_id')
@@ -167,19 +167,23 @@ class AccountReconcileModel(models.Model):
tax = self.env['account.tax'].browse(tax_res['id'])
new_aml_dicts.append({
'account_id': tax.account_id and tax.account_id.id or base_line_dict['account_id'],
'name': tax.name,
'account_id': tax_res['account_id'] or base_line_dict['account_id'],
'name': tax_res['name'],
'partner_id': base_line_dict.get('partner_id'),
'debit': tax_res['amount'] > 0 and tax_res['amount'] or 0,
'credit': tax_res['amount'] < 0 and -tax_res['amount'] or 0,
'analytic_account_id': tax.analytic and base_line_dict['analytic_account_id'],
'analytic_tag_ids': tax.analytic and base_line_dict['analytic_tag_ids'],
'tax_exigible': tax.tax_exigibility == 'on_payment',
'tax_exigible': tax_res['tax_exigibility'],
'tax_repartition_line_id': tax_res['tax_repartition_line_id'],
'tax_ids': tax_res['tax_ids'],
'tag_ids': tax_res['tag_ids']
})
# Handle price included taxes.
base_line_dict['debit'] = tax_res['base'] > 0 and tax_res['base'] or base_line_dict['debit']
base_line_dict['credit'] = tax_res['base'] < 0 and -tax_res['base'] or base_line_dict['credit']
base_line_dict['tag_ids'] = [(6, 0, res['base_tags'])]
return new_aml_dicts
@api.multi
@@ -456,7 +460,7 @@ class AccountReconcileModel(models.Model):
-- if there is a partner, propose all aml of the partner, otherwise propose only the ones
-- matching the statement line communication
AND
AND
(
(
line_partner.partner_id != 0
-1
View File
@@ -260,7 +260,6 @@ class ResPartner(models.Model):
def _invoice_total(self):
account_invoice_report = self.env['account.invoice.report']
if not self.ids:
self.total_invoiced = 0.0
return True
user_currency_id = self.env.user.company_id.currency_id.id
@@ -98,6 +98,12 @@ class TestReconciliationMatchingRules(AccountingTestCase):
'sequence': 1,
})
self.tax21 = self.env['account.tax'].create({
'name': '21%',
'type_tax_use': 'purchase',
'amount': 21,
})
def test_matching_fields(self):
''' Test all fields used to restrict the rules's applicability.'''
@@ -304,6 +310,34 @@ class TestReconciliationMatchingRules(AccountingTestCase):
{'partner_id': self.partner_2.id, 'debit': 1000.0, 'credit': 0.0},
])
def test_auto_reconcile_with_tax(self):
''' Test auto reconciliation with a tax amount included in the bank statement line'''
self.rule_1.write({
'auto_reconcile': True,
'force_tax_included': True,
'tax_ids': [(6, 0, self.tax21.ids)],
'rule_type': 'writeoff_suggestion',
})
self.bank_line_2.unlink()
self.bank_line_1.amount = -121
self._check_statement_matching(
self.rule_1,
{
self.bank_line_1.id: {'aml_ids': [], 'model': self.rule_1, 'status': 'reconciled'},
},
self.bank_st
)
# Check first line has been well reconciled.
self.assertRecordValues(self.bank_line_1.journal_entry_ids, [
{'partner_id': self.partner_1.id, 'debit': 0.0, 'credit': 121.0},
{'partner_id': self.partner_1.id, 'debit': 21.0, 'credit': 0.0, 'tax_line_id': self.tax21.id},
{'partner_id': self.partner_1.id, 'debit': 100.0, 'credit': 0.0, 'tax_ids': [self.tax21.id]}
])
def test_reverted_move_matching(self):
AccountMove = self.env['account.move']
move = AccountMove.create({
@@ -363,6 +363,27 @@
<field name="tax_base_amount" invisible="1"/> <!--Not to be removed, we need it so that the call to new() takes it into account when autocompleting the lines of a tax-->
<field name="tag_ids" invisible="1"/>
</tree>
<!-- Form view to cover mobile use -->
<form>
<group>
<field name="tax_line_id" invisible="1"/>
<field name="account_id" domain="[('company_id', '=', parent.company_id), ('deprecated', '=', False)]"/>
<field name="partner_id" domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/>
<field name="name"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="company_currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/>
<field name="debit" sum="Total Debit"/>
<field name="credit" sum="Total Credit"/>
<field name="tax_ids" string="Taxes Applied" widget="many2many_tags" domain="[('type_tax_use', '=?', parent.tax_type_domain)]" context="{'append_type_to_tax_name': not parent.tax_type_domain}" options="{'no_create': True}"/>
<field name="date_maturity" required="0" invisible="context.get('view_no_maturity', False)"/>
<field name="recompute_tax_line" invisible="1" readonly="1"/>
<field name="tax_line_grouping_key" invisible="1" readonly="1"/>
</group>
</form>
</field>
<field name="narration" colspan="4" placeholder="Add an internal note..." nolabel="1" height="50"/>
</page>
@@ -123,14 +123,32 @@ class AccountVoucher(models.Model):
@api.multi
@api.depends('tax_correction', 'line_ids.price_subtotal')
def _compute_total(self):
tax_calculation_rounding_method = self.env.user.company_id.tax_calculation_rounding_method
for voucher in self:
total = 0
tax_amount = 0
tax_lines_vals_merged = {}
for line in voucher.line_ids:
tax_info = line.tax_ids.compute_all(line.price_unit, voucher.currency_id, line.quantity, line.product_id, voucher.partner_id)
total += tax_info.get('total_included', 0.0)
tax_amount += sum([t.get('amount',0.0) for t in tax_info.get('taxes', False)])
voucher.amount = total + voucher.tax_correction
if tax_calculation_rounding_method == 'round_globally':
total += tax_info.get('total_excluded', 0.0)
for t in tax_info.get('taxes', False):
key = (
t['id'],
t['account_id'],
)
if key not in tax_lines_vals_merged:
tax_lines_vals_merged[key] = t.get('amount', 0.0)
else:
tax_lines_vals_merged[key] += t.get('amount', 0.0)
else:
total += tax_info.get('total_included', 0.0)
tax_amount += sum([t.get('amount', 0.0) for t in tax_info.get('taxes', False)])
if tax_calculation_rounding_method == 'round_globally':
tax_amount = sum([voucher.currency_id.round(t) for t in tax_lines_vals_merged.values()])
voucher.amount = total + tax_amount + voucher.tax_correction
else:
voucher.amount = total + voucher.tax_correction
voucher.tax_amount = tax_amount
@api.onchange('date')
+1 -1
View File
@@ -50,7 +50,7 @@ class LunchSupplier(models.Model):
country_id = fields.Many2one('res.country', related='partner_id.country_id', readonly=False)
responsible_id = fields.Many2one('res.users', string="Responsible", domain=lambda self: [('groups_id', 'in', self.env.ref('lunch.group_lunch_manager').id)],
default=lambda self: self.env.user.partner_id,
default=lambda self: self.env.user,
help="The responsible is the person that will order lunch for everyone"
"It will be used as the 'from' when sending the automatic email")
@@ -438,10 +438,27 @@ var BasicComposer = Widget.extend({
* @param {boolean} params.submitForm [optional]
*/
_processAttachmentChange: function (params) {
var self = this,
attachments = this.get('attachment_ids'),
files = params.files,
submitForm = params.submitForm;
var self = this;
var attachments = this.get('attachment_ids');
var files = params.files;
var submitForm = params.submitForm;
var $form = this.$('form.o_form_binary_form');
/**
* makes a new formData as formData.delete() is not supported by IE or Safari Mobile.
*
* @return {FormData}
*/
function makeFormDataWithoutUfile() {
var newFormData = new window.FormData();
$form.find('input').each(function (index, input) {
if (input.name !== 'ufile') {
newFormData.append(input.name, input.value);
}
});
return newFormData;
}
_.each(files, function (file) {
var attachment = _.findWhere(attachments, {
name: file.name,
@@ -453,24 +470,20 @@ var BasicComposer = Widget.extend({
attachments = _.without(attachments, attachment);
}
});
var $form = this.$('form.o_form_binary_form');
if (submitForm) {
$form.submit();
this._$attachmentButton.prop('disabled', true);
} else {
var data = new FormData($form[0]);
_.each(files, function (file) {
// removing existing key with blank data and appending again with file info
// In safari, existing key will not be updated when append with new file.
data.delete("ufile");
data.append("ufile", file, file.name);
var formData = makeFormDataWithoutUfile();
formData.append("ufile", file, file.name);
$.ajax({
url: $form.attr("action"),
type: "POST",
enctype: 'multipart/form-data',
processData: false,
contentType: false,
data: data,
data: formData,
success: function (result) {
var $el = $(result);
$.globalEval($el.contents().text());
+2 -2
View File
@@ -168,9 +168,9 @@ var RenameConversationDialog = Dialog.extend({
kwargs: {
name: name,
}
}).then(function (updatedName) {
}).then(function () {
var channel = self.call('mail_service', 'getThread', self._channelID);
channel.setName(updatedName);
channel.setName(name);
self._callback();
});
},
@@ -800,6 +800,7 @@ var Message = AbstractMessage.extend(Mixins.EventDispatcherMixin, ServicesMixin
_warnMessageModerated: function () {
var mailBus = this.call('mail_service', 'getMailBus');
if (this.needsModerationByUser()) {
this._setModeratedByUser(false);
var moderationBox = this.call('mail_service', 'getMailbox', 'moderation');
moderationBox.decrementMailboxCounter();
moderationBox.removeMessage(this.getID());
@@ -94,9 +94,7 @@ var MailManager = AbstractService.extend({
additionalThreadIDs: data.channel_ids
});
}
if (options.domain && options.domain !== []) {
this._addMessageToThreads(message, options);
}
this._addMessageToThreads(message, options);
}
return prom;
},
+1 -1
View File
@@ -1426,7 +1426,7 @@ QUnit.test('rename DM conversation', async function (assert) {
assert.step(args.method);
assert.strictEqual(args.args[0], 1);
assert.strictEqual(args.kwargs.name, "Demo");
return Promise.resolve("Demo");
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
+2 -2
View File
@@ -2325,9 +2325,9 @@ msgstr ""
#. module: mass_mailing
#. openerp-web
#: code:addons/mass_mailing/static/src/js/unsubscribe.js:49
#: code:addons/mass_mailing/static/src/js/unsubscribe.js:50
#, python-format
msgid "You have been <strong>successfully unsubscribed from </strong>."
msgid "You have been <strong>successfully unsubscribed from %s</strong>."
msgstr ""
#. module: mass_mailing
+2 -3
View File
@@ -95,7 +95,7 @@ class MailMail(models.Model):
return res
@api.multi
def _postprocess_sent_message(self, failure_type=None, **kwargs):
def _postprocess_sent_message(self, success_pids, failure_reason=False, failure_type=None):
mail_sent = not failure_type # we consider that a recipient error is a failure with mass mailling and show them as failed
for mail in self:
if mail.mailing_id:
@@ -103,5 +103,4 @@ class MailMail(models.Model):
mail.statistics_ids.write({'sent': fields.Datetime.now(), 'exception': False})
elif mail_sent is False and mail.statistics_ids:
mail.statistics_ids.write({'exception': fields.Datetime.now()})
return super(MailMail, self)._postprocess_sent_message(failure_type=failure_type, **kwargs)
return super(MailMail, self)._postprocess_sent_message(success_pids, failure_reason=failure_reason, failure_type=failure_type)
@@ -46,7 +46,10 @@ odoo.define('mass_mailing.unsubscribe', function (require) {
var unsubscribed_list = $("input[name='unsubscribed_list']").val();
if (unsubscribed_list){
$('#subscription_info').html(_t('You have been <strong>successfully unsubscribed from ' + unsubscribed_list + "</strong>."));
$('#subscription_info').html(_.str.sprintf(
_t("You have been <strong>successfully unsubscribed from %s</strong>."),
unsubscribed_list
));
}
else{
$('#subscription_info').html(_t('You have been <strong>successfully unsubscribed</strong>.'));
@@ -121,7 +121,7 @@
<header>
<div><title>Odoo</title></div>
<div class="text-center">
<img t-attf-src="/web/binary/company_logo"/>
<img t-attf-src="/web/binary/company_logo?company={{ res_company.id }}"/>
</div>
</header>
<div id="wrap" class="oe_structure oe_empty"/>
+5 -2
View File
@@ -817,8 +817,6 @@ class MrpProduction(models.Model):
moves_to_do = order.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
for move in moves_to_do.filtered(lambda m: m.product_qty == 0.0 and m.quantity_done > 0):
move.product_uom_qty = move.quantity_done
for move in moves_to_do.filtered(lambda m: float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding)):
move._action_cancel()
# MRP do not merge move, catch the result of _action_done in order
# to get extra moves.
moves_to_do = moves_to_do._action_done()
@@ -849,6 +847,11 @@ class MrpProduction(models.Model):
if wo.time_ids.filtered(lambda x: (not x.date_end) and (x.loss_type in ('productive', 'performance'))):
raise UserError(_('Work order %s is still running') % wo.name)
self._check_lots()
# Cancel unfinished move
move_to_cancel = self.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel') and float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding))
move_to_cancel._action_cancel()
self.post_inventory()
# Moves without quantity done are not posted => set them as done instead of canceling. In
# case the user edits the MO later on and sets some consumed quantity on those, we do not
+5 -3
View File
@@ -62,7 +62,8 @@ class PaymentAcquirerAuthorize(models.Model):
# The signature key is now '128-character hexadecimal format', while the
# transaction key was only 16-character.
if len(values['x_trans_key']) == 128:
return hmac.new(values['x_trans_key'].decode("hex").encode('utf-8'), data, hashlib.sha512).hexdigest().upper()
key = bytes.fromhex(values['x_trans_key'])
return hmac.new(key, data, hashlib.sha512).hexdigest().upper()
else:
return hmac.new(values['x_trans_key'].encode('utf-8'), data, hashlib.md5).hexdigest()
@@ -315,13 +316,14 @@ class TxAuthorize(models.Model):
'acquirer_reference': tree.get('x_trans_id'),
'date': fields.Datetime.now(),
})
if init_state != 'authorized':
self.execute_callback()
if self.payment_token_id:
self.payment_token_id.verified = True
self._set_transaction_done()
if init_state != 'authorized':
self.execute_callback()
if tree.get('x_type').lower() == 'auth_only':
self.write({'acquirer_reference': tree.get('x_trans_id')})
self._set_transaction_authorized()
+2 -8
View File
@@ -608,12 +608,7 @@ class PurchaseOrderLine(models.Model):
if product_lang.description_purchase:
self.name += '\n' + product_lang.description_purchase
fpos = self.order_id.fiscal_position_id
if self.env.uid == SUPERUSER_ID:
company_id = self.env.user.company_id.id
self.taxes_id = fpos.map_tax(self.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == company_id))
else:
self.taxes_id = fpos.map_tax(self.product_id.supplier_taxes_id)
self._compute_tax_id()
self._suggest_quantity()
self._onchange_quantity()
@@ -685,9 +680,8 @@ class PurchaseOrderLine(models.Model):
'''
if not self.product_id:
return
seller_min_qty = self.product_id.seller_ids\
.filtered(lambda r: r.name == self.order_id.partner_id)\
.filtered(lambda r: r.name == self.order_id.partner_id and (not r.product_id or r.product_id == self.product_id))\
.sorted(key=lambda r: r.min_qty)
if seller_min_qty:
self.product_qty = seller_min_qty[0].min_qty or 1.0
@@ -1,2 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order_employee,sale.order.employee.expense,sale.model_sale_order,base.group_user,1,0,0,0
access_sale_order_employee,sale.order.employee.expense,sale.model_sale_order,base.group_user,0,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order_employee sale.order.employee.expense sale.model_sale_order base.group_user 1 0 0 0 0
@@ -13,6 +13,7 @@
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="active" eval="False"/> <!-- opw-2027005: this rules breaks sale "see own document" -->
</record>
</data>
@@ -6,7 +6,7 @@
<field name="inherit_id" ref="hr_expense.hr_expense_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='employee_id']" position="after">
<field name="sale_order_id" context="{'sale_show_partner_name': True}"/>
<field name="sale_order_id" context="{'sale_show_partner_name': True}" groups="sales_team.group_sale_salesman"/>
</xpath>
</field>
</record>
@@ -17,7 +17,7 @@
<field name="inherit_id" ref="hr_expense.view_hr_expense_sheet_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='expense_line_ids']/tree/field[@name='name']" position="after">
<field name="sale_order_id" context="{'sale_show_partner_name': True}"/>
<field name="sale_order_id" context="{'sale_show_partner_name': True}" groups="sales_team.group_sale_salesman"/>
</xpath>
</field>
</record>
+1 -1
View File
@@ -184,7 +184,7 @@ class ProjectTask(models.Model):
@api.depends('project_id.sale_line_employee_ids')
def _compute_is_project_map_empty(self):
for task in self:
task.is_project_map_empty = not bool(task.project_id.sale_line_employee_ids)
task.is_project_map_empty = not bool(task.sudo().project_id.sale_line_employee_ids)
@api.onchange('project_id')
def _onchange_project(self):
@@ -760,7 +760,7 @@ function jSignatureClass(parent, options, instanceExtensions) {
// these, when enabled, will hover above the sig area. Hence we append them to DOM before canvas.
this.$controlbarUpper = (function(){
var controlbarstyle = 'padding:0 !important; margin:0 !important;'+
'width: 100% !important; height: 0 !important; -ms-touch-action: none;'+
'width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;'+
'margin-top:-1em !important; margin-bottom:1em !important;';
return $('<div style="'+controlbarstyle+'"></div>').appendTo($parent);
})();
@@ -771,7 +771,7 @@ function jSignatureClass(parent, options, instanceExtensions) {
this.$controlbarLower = (function(){
var controlbarstyle = 'padding:0 !important; margin:0 !important;'+
'width: 100% !important; height: 0 !important; -ms-touch-action: none;'+
'width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;'+
'margin-top:-1.5em !important; margin-bottom:1.5em !important; position: relative;';
return $('<div style="'+controlbarstyle+'"></div>').appendTo($parent);
})();
@@ -876,7 +876,7 @@ function jSignatureClass(parent, options, instanceExtensions) {
$canvas.bind('mouseup.'+apinamespace, drawEndHandler);
$canvas.bind('mousedown.'+apinamespace, drawStartHandler);
} else {
canvas.ontouchstart = function(e) {
canvas.addEventListener('touchstart', function(e) {
canvas.onmousedown = canvas.onmouseup = canvas.onmousemove = undef;
this.fatFingerCompensation = (
@@ -886,19 +886,19 @@ function jSignatureClass(parent, options, instanceExtensions) {
drawStartHandler(e);
canvas.ontouchend = drawEndHandler;
canvas.ontouchstart = drawStartHandler;
canvas.ontouchmove = drawMoveHandler;
};
canvas.onmousedown = function(e) {
canvas.addEventListener('touchend', drawEndHandler);
canvas.addEventListener('touchstart', drawStartHandler);
canvas.addEventListener('touchmove', drawMoveHandler);
});
canvas.addEventListener('mousedown', function(e) {
canvas.ontouchstart = canvas.ontouchend = canvas.ontouchmove = undef;
drawStartHandler(e);
canvas.onmousedown = drawStartHandler;
canvas.onmouseup = drawEndHandler;
canvas.onmousemove = drawMoveHandler;
}
canvas.addEventListener('mousedown', drawStartHandler);
canvas.addEventListener('mouseup', drawEndHandler);
canvas.addEventListener('mousemove', drawMoveHandler);
});
if (window.navigator.msPointerEnabled) {
canvas.onmspointerdown = drawStartHandler;
canvas.onmspointerup = drawEndHandler;
@@ -1084,28 +1084,16 @@ jSignatureClass.prototype.initializeCanvas = function(settings) {
settings.width = '100%';
}
$canvas.css(
'margin'
, 0
).css(
'padding'
, 0
).css(
'border'
, 'none'
).css(
'height'
, settings.height === 'ratio' || !settings.height ? 1 : settings.height.toString(10)
).css(
'width'
, settings.width === 'ratio' || !settings.width ? 1 : settings.width.toString(10)
).css(
'-ms-touch-action'
, 'none'
).css(
'background-color',
settings['background-color']
);
$canvas.css({
'margin': 0,
'padding': 0,
'border': 'none',
'height': settings.height === 'ratio' || !settings.height ? 1 : settings.height.toString(10),
'width': settings.width === 'ratio' || !settings.width ? 1 : settings.width.toString(10),
'-ms-touch-action': 'none',
'touch-action': 'none',
'background-color': settings['background-color'],
});
$canvas.appendTo(this.$parent);
+4
View File
@@ -41,6 +41,10 @@
});
// close modal on submit
$('.modal').on('submit', 'form', function (ev) {
var form = $(this).closest('form')[0];
if (form && form.checkValidity && !form.checkValidity()) {
return;
}
var modal = $(this).parentsUntil('body', '.modal');
if (modal.hasClass('o_database_backup')) {
$(modal).modal('hide');
+1 -1
View File
@@ -175,7 +175,7 @@ tour.register('rte_translator', {
}, {
content: "check bis: content is translated",
trigger: '#wrap p font:first:contains(translated french text)',
extra_trigger: 'body:not(:has(button[data-action=save]))',
extra_trigger: 'html[lang*="fr"] body:not(:has(button[data-action=save]))',
}, {
content: "check bis: placeholder translation",
trigger: 'input[placeholder="test french placeholder"]',
@@ -327,7 +327,7 @@ odoo.define('website_slides.quiz', function (require) {
_extractChannelData: function (slideData){
return {
id: slideData.channelId,
channelEnroll: slideData.enroll,
channelEnroll: slideData.channelEnroll,
signupAllowed: slideData.signupAllowed
};
},
+6
View File
@@ -1531,6 +1531,12 @@ class O2MForm(Form):
else:
self._values.update(proxy._records[index])
def _get_modifier(self, field, modifier, default=False, modmap=None, vals=None):
if vals is None:
vals = {**self._values, '•parent•': self._proxy._parent._values}
return super()._get_modifier(field, modifier, default=default, modmap=modmap, vals=vals)
def _onchange_values(self):
values = super(O2MForm, self)._onchange_values()
# computed o2m may not have a relation_field(?)