summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-12-04 17:11:57 +0100
committerChristophe Simonis <chs@odoo.com>2018-12-04 17:11:57 +0100
commitd4c65a7edee8ebe3c5b27d6eea42ebc119fbe085 (patch)
tree3e000dd98e9784b5eebca38019379fa167a0d49b /addons
parentc0eef4271110ebcab07811328a7b3f4309fb4d06 (diff)
parent2156af480fddc09b5897b349c04c4c9a2d67c802 (diff)
[MERGE] forward port branch 11.0 up to 2156af480fd
Diffstat (limited to 'addons')
-rw-r--r--addons/account/models/account_invoice.py2
-rw-r--r--addons/account/models/account_move.py2
-rw-r--r--addons/account/models/reconciliation_widget.py7
-rw-r--r--addons/account/report/account_aged_partner_balance.py2
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_action.js4
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js69
-rw-r--r--addons/account/static/tests/reconciliation_tests.js14
-rw-r--r--addons/account/wizard/account_reconcile.py2
-rw-r--r--addons/mrp/models/mrp_production.py1
-rw-r--r--addons/point_of_sale/static/src/js/screens.js18
-rw-r--r--addons/report_intrastat/report/report_intrastat_report.py6
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js1
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js45
-rw-r--r--addons/web_editor/static/src/js/editor/rte.js17
-rw-r--r--addons/web_editor/static/src/js/editor/snippets.editor.js1
-rw-r--r--addons/website/static/src/js/menu/edit.js41
-rw-r--r--addons/website_sale/models/product.py12
-rw-r--r--addons/website_sale/static/src/js/website_sale.js8
18 files changed, 218 insertions, 34 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 1b385897262..35ae3065467 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1424,7 +1424,7 @@ class AccountInvoice(models.Model):
res = {}
for line in self.tax_line_ids:
res.setdefault(line.tax_id.tax_group_id, {'base': 0.0, 'amount': 0.0})
- res[line.tax_id.tax_group_id]['amount'] += line.amount
+ res[line.tax_id.tax_group_id]['amount'] += line.amount_total
res[line.tax_id.tax_group_id]['base'] += line.base
res = sorted(res.items(), key=lambda l: l[0].sequence)
res = [(
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index cfe33be10ce..dc38e526693 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -481,7 +481,7 @@ class AccountMoveLine(models.Model):
# TODO: put the invoice link and partner_id on the account_move
invoice_id = fields.Many2one('account.invoice', oldname="invoice")
partner_id = fields.Many2one('res.partner', string='Partner', ondelete='restrict')
- user_type_id = fields.Many2one('account.account.type', related='account_id.user_type_id', index=True, store=True, oldname="user_type")
+ user_type_id = fields.Many2one('account.account.type', related='account_id.user_type_id', index=True, store=True, oldname="user_type", readonly=True)
tax_exigible = fields.Boolean(string='Appears in VAT report', default=True,
help="Technical field used to mark a tax line as exigible in the vat report or not (only exigible journal items are displayed). By default all new journal items are directly exigible, but with the feature cash_basis on taxes, some will become exigible only when the payment is recorded.")
parent_state = fields.Char(compute="_compute_parent_state", help="State of the parent account.move")
diff --git a/addons/account/models/reconciliation_widget.py b/addons/account/models/reconciliation_widget.py
index c30c4232a02..246f7268478 100644
--- a/addons/account/models/reconciliation_widget.py
+++ b/addons/account/models/reconciliation_widget.py
@@ -330,6 +330,12 @@ class AccountReconciliation(models.AbstractModel):
allowed_ids = set(Partner.browse(ids).ids)
rows = [row for row in rows if row['partner_id'] in allowed_ids]
+ # Keep mode for future use in JS
+ if res_type == 'account':
+ mode = 'accounts'
+ else:
+ mode = 'customers' if account_type == 'receivable' else 'suppliers'
+
# Fetch other data
for row in rows:
account = Account.browse(row['account_id'])
@@ -338,6 +344,7 @@ class AccountReconciliation(models.AbstractModel):
partner_id = is_partner and row['partner_id'] or None
rec_prop = self._get_move_line_reconciliation_proposition(account.id, partner_id)
row['reconciliation_proposition'] = self._prepare_move_lines(rec_prop, target_currency=currency)
+ row['mode'] = mode
row['company_id'] = account.company_id.id
# Return the partners with a reconciliation proposition first, since they are most likely to
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index 32aaa2e3085..4be77ed65b2 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -150,7 +150,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
partners_amount = {}
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
- for line in self.env['account.move.line'].browse(aml_ids):
+ for line in self.env['account.move.line'].browse(aml_ids).with_context(prefetch_fields=False):
partner_id = line.partner_id.id or False
if partner_id not in partners_amount:
partners_amount[partner_id] = 0.0
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js
index 5dbe30ba482..37844f00b3a 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_action.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js
@@ -43,6 +43,8 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, {
LineRenderer: ReconciliationRenderer.LineRenderer,
// used context params
params: ['statement_ids'],
+ // number of statements/partners/accounts to display
+ defaultDisplayQty: 10,
// number of moves lines displayed in 'match' mode
limitMoveLines: 5,
},
@@ -59,6 +61,7 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, {
this.params = params;
this.model = new this.config.Model(this, {
modelName: "account.reconciliation.widget",
+ defaultDisplayQty: params.params && params.params.defaultDisplayQty || this.config.defaultDisplayQty,
limitMoveLines: params.params && params.params.limitMoveLines || this.config.limitMoveLines,
});
this.widgets = [];
@@ -319,6 +322,7 @@ var ManualAction = StatementAction.extend({
ActionRenderer: ReconciliationRenderer.ManualRenderer,
LineRenderer: ReconciliationRenderer.ManualLineRenderer,
params: ['company_ids', 'mode', 'partner_ids', 'account_ids'],
+ defaultDisplayQty: 30,
limitMoveLines: 10,
},
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index 93c06fe63df..0ed5377c0b8 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -109,7 +109,7 @@ var StatementModel = BasicModel.extend({
this.valuenow = 0;
this.valuemax = 0;
this.alreadyDisplayed = [];
- this.defaultDisplayQty = 10;
+ this.defaultDisplayQty = options && options.defaultDisplayQty || 10;
this.limitMoveLines = options && options.limitMoveLines || 5;
},
@@ -1176,6 +1176,17 @@ var ManualModel = StatementModel.extend({
//--------------------------------------------------------------------------
/**
+ * Return a boolean telling if load button needs to be displayed or not
+ *
+ * @returns {boolean} true if load more button needs to be displayed
+ */
+ hasMoreLines: function () {
+ if (this.manualLines.length > this.pagerIndex) {
+ return true;
+ }
+ return false;
+ },
+ /**
* load data from
* - 'account.reconciliation.widget' fetch the lines to reconciliate
* - 'account.account' fetch all account code
@@ -1234,10 +1245,12 @@ var ManualModel = StatementModel.extend({
context: context,
})
.then(function (result) {
- var defs = _.map(result, self._formatLine.bind(self, context.mode));
+ self.manualLines = result;
self.valuenow = 0;
- self.valuemax = Object.keys(self.lines).length;
- return $.when.apply($, defs);
+ self.valuemax = Object.keys(self.manualLines).length;
+ var lines = self.manualLines.splice(0, self.defaultDisplayQty);
+ self.pagerIndex = lines.length;
+ return self.loadData(lines);
});
case 'accounts':
return self._rpc({
@@ -1247,10 +1260,12 @@ var ManualModel = StatementModel.extend({
context: context,
})
.then(function (result) {
- var defs = _.map(result, self._formatLine.bind(self, 'accounts'));
+ self.manualLines = result;
self.valuenow = 0;
- self.valuemax = Object.keys(self.lines).length;
- return $.when.apply($, defs);
+ self.valuemax = Object.keys(self.manualLines).length;
+ var lines = self.manualLines.splice(0, self.defaultDisplayQty);
+ self.pagerIndex = lines.length;
+ return self.loadData(lines);
});
default:
var partner_ids = context.partner_ids;
@@ -1266,17 +1281,47 @@ var ManualModel = StatementModel.extend({
context: context,
})
.then(function (result) {
- var defs = _.map(result.accounts, self._formatLine.bind(self, 'accounts'));
- defs = defs.concat(_.map(result.customers, self._formatLine.bind(self, 'customers')));
- defs = defs.concat(_.map(result.suppliers, self._formatLine.bind(self, 'suppliers')));
+ // Flatten the result
+ self.manualLines = [].concat(result.accounts, result.customers, result.suppliers)
self.valuenow = 0;
- self.valuemax = Object.keys(self.lines).length;
- return $.when.apply($, defs);
+ self.valuemax = Object.keys(self.manualLines).length;
+ var lines = self.manualLines.splice(0, self.defaultDisplayQty);
+ self.pagerIndex = lines.length;
+ return self.loadData(lines);
});
}
});
},
/**
+ * Load more partners/accounts
+ *
+ * @param {integer} qty quantity to load
+ * @returns {Deferred}
+ */
+ loadMore: function(qty) {
+ if (qty === undefined) {
+ qty = this.defaultDisplayQty;
+ }
+ var lines = this.manualLines.splice(this.pagerIndex, qty);
+ this.pagerIndex += qty;
+ return this.loadData(lines);
+ },
+ /**
+ * Method to load informations on lines
+ *
+ * @param {Array} lines manualLines to load
+ * @returns {Deferred}
+ */
+ loadData: function(lines) {
+ var self = this;
+ var defs = [];
+ _.each(lines, function (l) {
+ defs.push(self._formatLine(l.mode, l))
+ });
+ return $.when.apply($, defs);
+
+ },
+ /**
* Mark the account or the partner as reconciled
*
* @param {(string|string[])} handle
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 93a8a1818b4..38a72820223 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -436,12 +436,12 @@ var auto_reconciliation = {
var data_for_manual_reconciliation_widget = {
'[null,null]': {
'customers': [
- {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable"},
- {'account_id': 7, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable"}
+ {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'mode': "customers"},
+ {'account_id': 7, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'mode': "customers"}
],
'accounts': [
{
- 'account_id': 283, 'account_name': "101000 Current Assets", 'currency_id': 3, 'max_date': "2017-02-16 14:32:04", 'last_time_entries_checked': "2017-02-16", 'account_code': "101000",
+ 'account_id': 283, 'account_name': "101000 Current Assets", 'currency_id': 3, 'max_date': "2017-02-16 14:32:04", 'last_time_entries_checked': "2017-02-16", 'account_code': "101000", 'mode': "accounts",
'reconciliation_proposition': [
{'account_id': 283, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "BNK1/2017/0006: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "101000", 'ref': "", 'already_paid': false},
{'account_id': 283, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "INV/2017/0006", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 402, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "101000", 'ref': "", 'already_paid': false}
@@ -455,20 +455,20 @@ var data_for_manual_reconciliation_widget = {
{'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "BNK1/999: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 999, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
{'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "INV/998", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 998, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
],
- 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 8, 'account_name': "Account Payable"
+ 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 8, 'account_name': "Account Payable", 'mode': "suppliers"
}, {
'account_id': 284, 'partner_name': "Camptocamp",
'reconciliation_proposition': [
{'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 12, 'account_name': "101000 Current Assets", 'name': "BNK1/1999: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 1999, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
{'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 12, 'account_name': "101000 Current Assets", 'name': "INV/1998", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 1998, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
],
- 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 12, 'account_name': "Account Payable"
+ 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 12, 'account_name': "Account Payable", 'mode': "suppliers"
}
]
},
'["partner",null,"receivable"]': [
- {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable"},
- {'account_id': 287, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable"}
+ {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'mode': "customers"},
+ {'account_id': 287, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'mode': "customers"}
]
};
diff --git a/addons/account/wizard/account_reconcile.py b/addons/account/wizard/account_reconcile.py
index ea6aa9caae4..5706ada741f 100644
--- a/addons/account/wizard/account_reconcile.py
+++ b/addons/account/wizard/account_reconcile.py
@@ -39,7 +39,7 @@ class AccountMoveLineReconcile(models.TransientModel):
credit += line.credit
debit += line.debit
precision = self.env.user.company_id.currency_id.decimal_places
- writeoff = float_round(debit - credit, precision_digits=precision)
+ writeoff = float_round(debit - credit, precision_digits=precision) + 0.0 #remove -0.0 result
credit = float_round(credit, precision_digits=precision)
debit = float_round(debit, precision_digits=precision)
return {'trans_nbr': len(lines), 'credit': credit, 'debit': debit, 'writeoff': writeoff}
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index de04da82b7f..39ebef74e21 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -439,6 +439,7 @@ class MrpProduction(models.Model):
if move:
if quantity > 0:
move[0].write({'product_uom_qty': quantity})
+ move.unit_factor = quantity / move.raw_material_production_id.product_qty
elif quantity < 0: # Do not remove 0 lines
if move[0].quantity_done > 0:
raise UserError(_('Lines need to be deleted, but can not as you still have some quantities to consume in them. '))
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 63aff0fc7ef..f4ff433bf71 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -1558,11 +1558,21 @@ var ReceiptScreenWidget = ScreenWidget.extend({
};
},
print_web: function() {
- if($.browser.safari){
+ if ($.browser.safari) {
document.execCommand('print', false, null);
- }
- else{
- window.print();
+ } else {
+ try {
+ window.print();
+ } catch(err) {
+ if (navigator.userAgent.toLowerCase().indexOf("android") > -1) {
+ this.gui.show_popup('error',{
+ 'title':_t('Printing is not supported on some android browsers'),
+ 'body': _t('Printing is not supported on some android browsers due to no default printing protocol is available. It is possible to print your tickets by making use of an IoT Box.'),
+ });
+ } else {
+ throw err;
+ }
+ }
}
this.pos.get_order()._printed = true;
},
diff --git a/addons/report_intrastat/report/report_intrastat_report.py b/addons/report_intrastat/report/report_intrastat_report.py
index 2e274b3ec3b..83f412fd06e 100644
--- a/addons/report_intrastat/report/report_intrastat_report.py
+++ b/addons/report_intrastat/report/report_intrastat_report.py
@@ -36,12 +36,12 @@ class ReportIntrastat(models.Model):
intrastat.id as intrastat_id,
upper(inv_country.code) as code,
sum(case when inv_line.price_unit is not null
- then inv_line.price_unit * inv_line.quantity
+ then inv_line.price_unit * (1.0 - coalesce(inv_line.discount, 0.0) / 100.0) * inv_line.quantity
else 0
end) as value,
sum(
- case when uom.category_id != puom.category_id then (pt.weight * inv_line.quantity)
- else (pt.weight * inv_line.quantity * uom.factor) end
+ case when uom.category_id != puom.category_id then (coalesce(nullif(pp.weight, 0), pt.weight) * inv_line.quantity)
+ else (coalesce(nullif(pp.weight, 0), pt.weight) * inv_line.quantity * uom.factor) end
) as weight,
sum(
case when uom.category_id != puom.category_id then inv_line.quantity
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 69fbe44b14b..1e5df5ef030 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -3241,6 +3241,7 @@ var BasicModel = AbstractModel.extend({
_.each(element._changes, function (command) {
if (command.operation === 'DELETE' ||
command.operation === 'FORGET' ||
+ (command.operation === 'ADD' && !command.isNew)||
command.operation === 'REMOVE_ALL') {
return;
}
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 61aaa29a664..875a6031f66 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -6246,6 +6246,51 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('one2many, onchange, edition and multipage...', function (assert) {
+ assert.expect(7);
+
+ this.data.partner.onchanges = {
+ turtles: function (obj) {
+ obj.turtles = [[5]].concat(obj.turtles);
+ }
+ };
+
+ this.data.partner.records[0].turtles = [1,2,3];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch:'<form string="Partners">' +
+ '<field name="turtles">' +
+ '<tree editable="bottom" limit="2">' +
+ '<field name="turtle_foo"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ assert.step(args.method + ' ' + args.model)
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+ form.$('.o_field_x2many_list_row_add a').click();
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ assert.verifySteps([
+ 'read partner',
+ 'read turtle',
+ 'default_get turtle',
+ 'onchange partner',
+ 'default_get turtle',
+ 'onchange partner',
+ ]);
+ form.destroy();
+ });
+
QUnit.test('one2many with CREATE onchanges correctly refreshed', function (assert) {
assert.expect(5);
diff --git a/addons/web_editor/static/src/js/editor/rte.js b/addons/web_editor/static/src/js/editor/rte.js
index 1e745d7cceb..485edaad13c 100644
--- a/addons/web_editor/static/src/js/editor/rte.js
+++ b/addons/web_editor/static/src/js/editor/rte.js
@@ -42,7 +42,10 @@ var History = function History($editable) {
$editable.removeAttr('contentEditable').removeProp('contentEditable');
}
+ $editable.trigger('content_will_be_destroyed');
$editable.html(oSnap.contents).scrollTop(oSnap.scrollTop);
+ $editable.trigger('content_was_recreated');
+
$('.oe_overlay').remove();
$('.note-control-selection').hide();
@@ -290,6 +293,20 @@ var RTEWidget = Widget.extend({
var $editable = this.editable();
+ // When a undo/redo is performed, the whole DOM is changed so we have
+ // to prepare for it (website will restart animations for example)
+ // TODO should be better handled
+ $editable.on('content_will_be_destroyed', function (ev) {
+ self.trigger_up('content_will_be_destroyed', {
+ $target: $(ev.currentTarget),
+ });
+ });
+ $editable.on('content_was_recreated', function (ev) {
+ self.trigger_up('content_was_recreated', {
+ $target: $(ev.currentTarget),
+ });
+ });
+
$editable.addClass('o_editable')
.data('rte', this)
.each(function () {
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index 999131020bf..2ec8506321c 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -340,6 +340,7 @@ var SnippetEditor = Widget.extend({
}
},
});
+ this.trigger_up('snippet_cloned', {$target: $clone});
},
/**
* Called when the overlay dimensions/positions should be recomputed.
diff --git a/addons/website/static/src/js/menu/edit.js b/addons/website/static/src/js/menu/edit.js
index 0397b1f8aca..40da42a82d4 100644
--- a/addons/website/static/src/js/menu/edit.js
+++ b/addons/website/static/src/js/menu/edit.js
@@ -17,6 +17,9 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
edit: '_startEditMode',
}),
custom_events: _.extend({}, websiteNavbarData.WebsiteNavbarActionWidget.custom_events || {}, {
+ content_will_be_destroyed: '_onContentWillBeDestroyed',
+ content_was_recreated: '_onContentWasRecreated',
+ snippet_cloned: '_onSnippetCloned',
snippet_dropped: '_onSnippetDropped',
}),
@@ -88,6 +91,44 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
//--------------------------------------------------------------------------
/**
+ * Called when content will be destroyed in the page. Notifies the
+ * WebsiteRoot that is should stop the animations.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onContentWillBeDestroyed: function (ev) {
+ this.trigger_up('animation_stop_demand', {
+ $target: ev.data.$target,
+ });
+ },
+ /**
+ * Called when content will be recreated in the page. Notifies the
+ * WebsiteRoot that is should start the animations.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onContentWasRecreated: function (ev) {
+ this.trigger_up('animation_start_demand', {
+ editableMode: true,
+ $target: ev.data.$target,
+ });
+ },
+ /**
+ * Called when a snippet is cloned in the page. Notifies the WebsiteRoot
+ * that is should start the animations for this snippet.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onSnippetCloned: function (ev) {
+ this.trigger_up('animation_start_demand', {
+ editableMode: true,
+ $target: ev.data.$target,
+ });
+ },
+ /**
* Called when a snippet is dropped in the page. Notifies the WebsiteRoot
* that is should start the animations for this snippet.
*
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index ba95563d790..465fcb1d153 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -5,7 +5,7 @@ from odoo.addons import decimal_precision as dp
from odoo.tools import pycompat
from odoo.tools.translate import html_translate
-from odoo.tools import float_is_zero
+from odoo.tools import float_compare
class ProductStyle(models.Model):
@@ -199,8 +199,14 @@ class Product(models.Model):
for p, p2 in pycompat.izip(self, self2):
taxes = partner.property_account_position_id.map_tax(p.sudo().taxes_id.filtered(lambda x: x.company_id == company_id), p, partner)
p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret]
- price_without_pricelist = taxes.compute_all(p.list_price, pricelist.currency_id)[ret]
- p.website_price_difference = False if float_is_zero(price_without_pricelist - p.website_price, precision_rounding=pricelist.currency_id.rounding) else True
+ # We must convert the price_without_pricelist in the same currency than the
+ # website_price, otherwise the comparison doesn't make sense. Moreover, we show a price
+ # difference only if the website price is lower
+ price_without_pricelist = p.list_price
+ if company_id.currency_id != pricelist.currency_id:
+ price_without_pricelist = company_id.currency_id.compute(price_without_pricelist, pricelist.currency_id)
+ price_without_pricelist = taxes.compute_all(price_without_pricelist, pricelist.currency_id)[ret]
+ p.website_price_difference = True if float_compare(price_without_pricelist, p.website_price, precision_rounding=pricelist.currency_id.rounding) > 0 else False
p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
@api.multi
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 3f325be94ec..86ed21a7312 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -90,6 +90,9 @@ odoo.define('website_sale.website_sale', function (require) {
var product_ids = [];
var product_dom = $(event.target).closest(".js_product").find(".js_add_cart_variants");
var qty = $(event.target).closest('form').find('input[name="add_qty"]').val();
+ if ($('body').hasClass('editor_enable')) {
+ return;
+ }
var attribute_value_ids = product_dom.data("attribute_value_ids");
_.each(attribute_value_ids, function(entry) {
product_ids.push(entry[0]);
@@ -123,7 +126,7 @@ odoo.define('website_sale.website_sale', function (require) {
$(oe_website_sale).on("change", ".oe_cart input.js_quantity[data-product-id]", function () {
var $input = $(this);
- if ($input.data('update_change')) {
+ if ($input.data('update_change') || $('body').hasClass('editor_enable')) {
return;
}
var value = parseInt($input.val() || 0, 10);
@@ -192,6 +195,9 @@ odoo.define('website_sale.website_sale', function (require) {
// hack to add and remove from cart with json
$(oe_website_sale).on('click', 'a.js_add_cart_json', function (ev) {
+ if ($('body').hasClass('editor_enable')) {
+ return;
+ }
ev.preventDefault();
var $link = $(ev.currentTarget);
var $input = $link.parent().find("input");