summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-02-20 17:37:22 +0100
committerChristophe Simonis <chs@odoo.com>2019-02-20 17:37:22 +0100
commitdb1c7765cba0692cfc4eda34ead000b048da4dae (patch)
tree34e351e1714858f9b607e216f505b3e4ce453115 /addons
parent7151f98358fc3cab13c1cc7400c5e0b58d662978 (diff)
parent2c495502aa37374f3748d9ff4ab7cf9e8d9a6d6e (diff)
[MERGE] forward port branch saas-11.3 up to 2c495502aa3
Diffstat (limited to 'addons')
-rw-r--r--addons/account/models/account_move.py229
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js4
-rw-r--r--addons/account/views/res_config_settings_views.xml1
-rw-r--r--addons/account_voucher/models/account_voucher.py29
-rw-r--r--addons/base_automation/views/base_automation_view.xml3
-rw-r--r--addons/base_import_module/views/base_import_module_view.xml1
-rw-r--r--addons/crm/views/crm_lead_views.xml2
-rw-r--r--addons/google_calendar/models/google_calendar.py4
-rw-r--r--addons/hr_holidays/models/hr_leave_type.py5
-rw-r--r--addons/im_livechat/models/im_livechat_channel.py1
-rw-r--r--addons/l10n_it/__manifest__.py2
-rw-r--r--addons/l10n_mx/data/account_data.xml4
-rw-r--r--addons/l10n_mx/data/account_tax_data.xml34
-rw-r--r--addons/mass_mailing/models/mass_mailing.py6
-rw-r--r--addons/point_of_sale/models/pos_order.py10
-rw-r--r--addons/point_of_sale/static/src/js/gui.js6
-rw-r--r--addons/sale_crm/models/crm_lead.py2
-rw-r--r--addons/stock/models/product.py4
-rw-r--r--addons/stock/report/report_deliveryslip.xml12
-rw-r--r--addons/stock/views/stock_picking_views.xml7
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js6
-rw-r--r--addons/web/static/src/js/views/search/search_filters.js2
-rw-r--r--addons/web/static/tests/views/pivot_tests.js89
-rw-r--r--addons/website_sale_delivery/static/src/js/website_sale_delivery.js6
24 files changed, 342 insertions, 127 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 905cbc47679..688f81c38ea 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -393,18 +393,20 @@ class AccountMove(models.Model):
@api.multi
def _reverse_move(self, date=None, journal_id=None, auto=False):
self.ensure_one()
- reversed_move = self.copy(default={
- 'date': date,
- 'journal_id': journal_id.id if journal_id else self.journal_id.id,
- 'ref': (_('Automatic reversal of: %s') if auto else _('Reversal of: %s')) % (self.name),
- 'auto_reverse': False})
- for acm_line in reversed_move.line_ids.with_context(check_move_validity=False):
- acm_line.write({
- 'debit': acm_line.credit,
- 'credit': acm_line.debit,
- 'amount_currency': -acm_line.amount_currency
- })
- self.reverse_entry_id = reversed_move
+ with self.env.norecompute():
+ reversed_move = self.copy(default={
+ 'date': date,
+ 'journal_id': journal_id.id if journal_id else self.journal_id.id,
+ 'ref': (_('Automatic reversal of: %s') if auto else _('Reversal of: %s')) % (self.name),
+ 'auto_reverse': False})
+ for acm_line in reversed_move.line_ids.with_context(check_move_validity=False):
+ acm_line.write({
+ 'debit': acm_line.credit,
+ 'credit': acm_line.debit,
+ 'amount_currency': -acm_line.amount_currency
+ })
+ self.reverse_entry_id = reversed_move
+ self.recompute()
return reversed_move
@api.multi
@@ -533,7 +535,7 @@ class AccountMoveLine(models.Model):
#computing the `reconciled` field.
reconciled = False
digits_rounding_precision = line.company_id.currency_id.rounding
- if float_is_zero(amount, precision_rounding=digits_rounding_precision):
+ if (line.matched_debit_ids or line.matched_credit_ids) and float_is_zero(amount, precision_rounding=digits_rounding_precision):
if line.currency_id and line.amount_currency:
if float_is_zero(amount_residual_currency, precision_rounding=line.currency_id.rounding):
reconciled = True
@@ -914,7 +916,7 @@ class AccountMoveLine(models.Model):
for line in self:
company_ids.add(line.company_id.id)
all_accounts.append(line.account_id)
- if line.reconciled:
+ if (line.matched_debit_ids or line.matched_credit_ids) and line.reconciled:
raise UserError(_('You are trying to reconcile some entries that are already reconciled.'))
if len(company_ids) > 1:
raise UserError(_('To reconcile the entries company should be the same for all entries.'))
@@ -1062,6 +1064,56 @@ class AccountMoveLine(models.Model):
)
return rec_move_ids.unlink()
+ def _apply_taxes(self, vals, amount):
+ tax_lines_vals = []
+ # Get ids from triplets : https://www.odoo.com/documentation/10.0/reference/orm.html#odoo.models.Model.write
+ tax_ids = [tax['id'] for tax in self.resolve_2many_commands('tax_ids', vals['tax_ids']) if tax.get('id')]
+ # Since create() receives ids instead of recordset, let's just use the old-api bridge
+ taxes = self.env['account.tax'].browse(tax_ids)
+ currency = self.env['res.currency'].browse(vals.get('currency_id'))
+ partner = self.env['res.partner'].browse(vals.get('partner_id'))
+ ctx = dict(self._context)
+ ctx['round'] = ctx.get('round', True)
+ res = taxes.with_context(ctx).compute_all(amount,
+ currency, 1, vals.get('product_id'), partner)
+ # Adjust line amount if any tax is price_include
+ if abs(res['total_excluded']) < abs(amount):
+ if vals['debit'] != 0.0: vals['debit'] = res['total_excluded']
+ if vals['credit'] != 0.0: vals['credit'] = -res['total_excluded']
+ if vals.get('amount_currency'):
+ vals['amount_currency'] = self.env['res.currency'].browse(vals['currency_id']).round(vals['amount_currency'] * (res['total_excluded']/amount))
+ # Create tax lines
+ for tax_vals in res['taxes']:
+ if tax_vals['amount']:
+ tax = self.env['account.tax'].browse([tax_vals['id']])
+ account_id = (amount > 0 and tax_vals['account_id'] or tax_vals['refund_account_id'])
+ if not account_id: account_id = vals['account_id']
+ temp = {
+ 'account_id': account_id,
+ 'name': vals['name'] + ' ' + tax_vals['name'],
+ 'tax_line_id': tax_vals['id'],
+ 'move_id': vals['move_id'],
+ 'partner_id': vals.get('partner_id'),
+ 'statement_id': vals.get('statement_id'),
+ 'debit': tax_vals['amount'] > 0 and tax_vals['amount'] or 0.0,
+ 'credit': tax_vals['amount'] < 0 and -tax_vals['amount'] or 0.0,
+ 'analytic_account_id': vals.get('analytic_account_id') if tax.analytic else False,
+ }
+ bank = self.env["account.bank.statement.line"].browse(vals.get('statement_line_id')).statement_id
+ if bank.currency_id != bank.company_id.currency_id:
+ ctx = {}
+ if 'date' in vals:
+ ctx['date'] = vals['date']
+ elif 'date_maturity' in vals:
+ ctx['date'] = vals['date_maturity']
+ temp['currency_id'] = bank.currency_id.id
+ temp['amount_currency'] = bank.company_id.currency_id.with_context(ctx).compute(tax_vals['amount'], bank.currency_id, round=True)
+ if vals.get('tax_exigible'):
+ temp['tax_exigible'] = True
+ temp['account_id'] = tax.cash_basis_account.id or account_id
+ tax_lines_vals.append(temp)
+ return tax_lines_vals
+
####################################################
# CRUD methods
####################################################
@@ -1495,90 +1547,91 @@ class AccountPartialReconcile(models.Model):
self.ensure_one()
move_date = self.debit_move_id.date
newly_created_move = self.env['account.move']
- for move in (self.debit_move_id.move_id, self.credit_move_id.move_id):
- #move_date is the max of the 2 reconciled items
- if move_date < move.date:
- move_date = move.date
- percentage_before = percentage_before_rec[move.id]
- percentage_after = move.line_ids[0]._get_matched_percentage()[move.id]
- # update the percentage before as the move can be part of
- # multiple partial reconciliations
- percentage_before_rec[move.id] = percentage_after
-
- for line in move.line_ids:
- if not line.tax_exigible:
- #amount is the current cash_basis amount minus the one before the reconciliation
- amount = line.balance * percentage_after - line.balance * percentage_before
- rounded_amt = self._get_amount_tax_cash_basis(amount, line)
- if float_is_zero(rounded_amt, precision_rounding=line.company_id.currency_id.rounding):
- continue
- if line.tax_line_id and line.tax_line_id.tax_exigibility == 'on_payment':
- if not newly_created_move:
- newly_created_move = self._create_tax_basis_move()
- #create cash basis entry for the tax line
- to_clear_aml = self.env['account.move.line'].with_context(check_move_validity=False).create({
- 'name': line.move_id.name,
- 'debit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
- 'credit': rounded_amt if rounded_amt > 0 else 0.0,
- 'account_id': line.account_id.id,
- 'analytic_account_id': line.analytic_account_id.id,
- 'analytic_tag_ids': line.analytic_tag_ids.ids,
- 'tax_exigible': True,
- 'amount_currency': line.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0,
- 'currency_id': line.currency_id.id,
- 'move_id': newly_created_move.id,
- 'partner_id': line.partner_id.id,
- })
- # Group by cash basis account and tax
- self.env['account.move.line'].with_context(check_move_validity=False).create({
- 'name': line.name,
- 'debit': rounded_amt if rounded_amt > 0 else 0.0,
- 'credit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
- 'account_id': line.tax_line_id.cash_basis_account_id.id,
- 'analytic_account_id': line.analytic_account_id.id,
- 'analytic_tag_ids': line.analytic_tag_ids.ids,
- 'tax_line_id': line.tax_line_id.id,
- 'tax_exigible': True,
- 'amount_currency': line.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0,
- 'currency_id': line.currency_id.id,
- 'move_id': newly_created_move.id,
- 'partner_id': line.partner_id.id,
- })
- if line.account_id.reconcile:
- #setting the account to allow reconciliation will help to fix rounding errors
- to_clear_aml |= line
- to_clear_aml.reconcile()
-
- if any([tax.tax_exigibility == 'on_payment' for tax in line.tax_ids]):
- if not newly_created_move:
- newly_created_move = self._create_tax_basis_move()
- #create cash basis entry for the base
- for tax in line.tax_ids.filtered(lambda t: t.tax_exigibility == 'on_payment'):
- account_id = self._get_tax_cash_basis_base_account(line, tax)
- self.env['account.move.line'].with_context(check_move_validity=False).create({
- 'name': line.name,
- 'debit': rounded_amt > 0 and rounded_amt or 0.0,
- 'credit': rounded_amt < 0 and abs(rounded_amt) or 0.0,
- 'account_id': account_id.id,
+ with self.env.norecompute():
+ for move in (self.debit_move_id.move_id, self.credit_move_id.move_id):
+ #move_date is the max of the 2 reconciled items
+ if move_date < move.date:
+ move_date = move.date
+ percentage_before = percentage_before_rec[move.id]
+ percentage_after = move.line_ids[0]._get_matched_percentage()[move.id]
+ # update the percentage before as the move can be part of
+ # multiple partial reconciliations
+ percentage_before_rec[move.id] = percentage_after
+
+ for line in move.line_ids:
+ if not line.tax_exigible:
+ #amount is the current cash_basis amount minus the one before the reconciliation
+ amount = line.balance * percentage_after - line.balance * percentage_before
+ rounded_amt = self._get_amount_tax_cash_basis(amount, line)
+ if float_is_zero(rounded_amt, precision_rounding=line.company_id.currency_id.rounding):
+ continue
+ if line.tax_line_id and line.tax_line_id.tax_exigibility == 'on_payment':
+ if not newly_created_move:
+ newly_created_move = self._create_tax_basis_move()
+ #create cash basis entry for the tax line
+ to_clear_aml = self.env['account.move.line'].with_context(check_move_validity=False).create({
+ 'name': line.move_id.name,
+ 'debit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
+ 'credit': rounded_amt if rounded_amt > 0 else 0.0,
+ 'account_id': line.account_id.id,
+ 'analytic_account_id': line.analytic_account_id.id,
+ 'analytic_tag_ids': line.analytic_tag_ids.ids,
'tax_exigible': True,
- 'tax_ids': [(6, 0, [tax.id])],
- 'move_id': newly_created_move.id,
+ 'amount_currency': line.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0,
'currency_id': line.currency_id.id,
- 'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0,
+ 'move_id': newly_created_move.id,
'partner_id': line.partner_id.id,
})
+ # Group by cash basis account and tax
self.env['account.move.line'].with_context(check_move_validity=False).create({
'name': line.name,
- 'credit': rounded_amt > 0 and rounded_amt or 0.0,
- 'debit': rounded_amt < 0 and abs(rounded_amt) or 0.0,
- 'account_id': account_id.id,
+ 'debit': rounded_amt if rounded_amt > 0 else 0.0,
+ 'credit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
+ 'account_id': line.tax_line_id.cash_basis_account_id.id,
+ 'analytic_account_id': line.analytic_account_id.id,
+ 'analytic_tag_ids': line.analytic_tag_ids.ids,
+ 'tax_line_id': line.tax_line_id.id,
'tax_exigible': True,
- 'move_id': newly_created_move.id,
+ 'amount_currency': line.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0,
'currency_id': line.currency_id.id,
- 'amount_currency': self.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0,
+ 'move_id': newly_created_move.id,
'partner_id': line.partner_id.id,
})
-
+ if line.account_id.reconcile:
+ #setting the account to allow reconciliation will help to fix rounding errors
+ to_clear_aml |= line
+ to_clear_aml.reconcile()
+
+ if any([tax.tax_exigibility == 'on_payment' for tax in line.tax_ids]):
+ if not newly_created_move:
+ newly_created_move = self._create_tax_basis_move()
+ #create cash basis entry for the base
+ for tax in line.tax_ids.filtered(lambda t: t.tax_exigibility == 'on_payment'):
+ account_id = self._get_tax_cash_basis_base_account(line, tax)
+ self.env['account.move.line'].with_context(check_move_validity=False).create({
+ 'name': line.name,
+ 'debit': rounded_amt > 0 and rounded_amt or 0.0,
+ 'credit': rounded_amt < 0 and abs(rounded_amt) or 0.0,
+ 'account_id': account_id.id,
+ 'tax_exigible': True,
+ 'tax_ids': [(6, 0, [tax.id])],
+ 'move_id': newly_created_move.id,
+ 'currency_id': line.currency_id.id,
+ 'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0,
+ 'partner_id': line.partner_id.id,
+ })
+ self.env['account.move.line'].with_context(check_move_validity=False).create({
+ 'name': line.name,
+ 'credit': rounded_amt > 0 and rounded_amt or 0.0,
+ 'debit': rounded_amt < 0 and abs(rounded_amt) or 0.0,
+ 'account_id': account_id.id,
+ 'tax_exigible': True,
+ 'move_id': newly_created_move.id,
+ 'currency_id': line.currency_id.id,
+ 'amount_currency': self.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0,
+ 'partner_id': line.partner_id.id,
+ })
+ self.recompute()
if newly_created_move:
if move_date > (self.company_id.period_lock_date or date.min) and newly_created_move.date != move_date:
# The move date should be the maximum date between payment and invoice (in case
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index 6b79d07a63a..ede81ea2043 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -900,11 +900,11 @@ var StatementModel = BasicModel.extend({
});
var company_currency = session.get_currency(line.st_line.currency_id);
var company_precision = company_currency && company_currency.digits[1] || 2;
- total = utils.round_decimals(total*1000, company_precision)/1000 || 0;
+ total = utils.round_decimals(total, company_precision) || 0;
if(isOtherCurrencyId){
var other_currency = session.get_currency(isOtherCurrencyId);
var other_precision = other_currency && other_currency.digits[1] || 2;
- amount_currency = utils.round_decimals(amount_currency, other_precision)
+ amount_currency = utils.round_decimals(amount_currency, other_precision);
}
line.balance = {
amount: total,
diff --git a/addons/account/views/res_config_settings_views.xml b/addons/account/views/res_config_settings_views.xml
index 44854c80d66..18d361cd629 100644
--- a/addons/account/views/res_config_settings_views.xml
+++ b/addons/account/views/res_config_settings_views.xml
@@ -96,7 +96,6 @@
</div>
<div class="o_setting_right_pane" name="account_taxcloud_right_pane">
<label for="module_account_taxcloud" string="TaxCloud"/>
- <span class="fa fa-lg fa-building-o" title="Values set here are company-specific." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
<div class="text-muted">
Compute tax rates based on U.S. ZIP codes
</div>
diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py
index 0245292108e..14161aa4251 100644
--- a/addons/account_voucher/models/account_voucher.py
+++ b/addons/account_voucher/models/account_voucher.py
@@ -274,6 +274,8 @@ class AccountVoucher(models.Model):
:return: Tuple build as (remaining amount not allocated on voucher lines, list of account_move_line created in this method)
:rtype: tuple(float, list of int)
'''
+ tax_calculation_rounding_method = self.env.user.company_id.tax_calculation_rounding_method
+ tax_lines_vals = []
for line in self.line_ids:
#create one move line per voucher line where amount is not 0.0
if not line.price_subtotal:
@@ -331,6 +333,33 @@ class AccountVoucher(models.Model):
self.env['account.move.line'].create(temp)
self.env['account.move.line'].create(move_line)
+ # When global rounding is activated, we must wait until all tax lines are computed to
+ # merge them.
+ if tax_calculation_rounding_method == 'round_globally':
+ tax_lines_vals += self.env['account.move.line'].with_context(round=False)._apply_taxes(
+ move_line,
+ move_line.get('debit', 0.0) - move_line.get('credit', 0.0)
+ )
+
+ # When round globally is set, we merge the tax lines
+ if tax_calculation_rounding_method == 'round_globally':
+ tax_lines_vals_merged = {}
+ for tax_line_vals in tax_lines_vals:
+ key = (
+ tax_line_vals['tax_line_id'],
+ tax_line_vals['account_id'],
+ tax_line_vals['analytic_account_id'],
+ )
+ if key not in tax_lines_vals_merged:
+ tax_lines_vals_merged[key] = tax_line_vals
+ else:
+ tax_lines_vals_merged[key]['debit'] += tax_line_vals['debit']
+ tax_lines_vals_merged[key]['credit'] += tax_line_vals['credit']
+ currency = self.env['res.currency'].browse(company_currency)
+ for vals in tax_lines_vals_merged.values():
+ vals['debit'] = currency.round(vals['debit'])
+ vals['credit'] = currency.round(vals['credit'])
+ self.env['account.move.line'].create(vals)
return line_total
@api.multi
diff --git a/addons/base_automation/views/base_automation_view.xml b/addons/base_automation/views/base_automation_view.xml
index 206f7897c75..35875ccc3ff 100644
--- a/addons/base_automation/views/base_automation_view.xml
+++ b/addons/base_automation/views/base_automation_view.xml
@@ -11,9 +11,6 @@
<xpath expr="//button[@name='create_action']" position="replace">
</xpath>
<xpath expr="//button[@name='unlink_action']" position="replace">
- <button name="unlink_action_for_menu" string="Remove Action" type="object"
- attrs="{'invisible':[('binding_model_id','=',False)]}"
- help="Remove the contextual action related to this server action"/>
</xpath>
<div class="oe_title" position="before">
<div class="oe_button_box" name="button_box">
diff --git a/addons/base_import_module/views/base_import_module_view.xml b/addons/base_import_module/views/base_import_module_view.xml
index fb18fdfbf49..39c8f5bc15d 100644
--- a/addons/base_import_module/views/base_import_module_view.xml
+++ b/addons/base_import_module/views/base_import_module_view.xml
@@ -8,6 +8,7 @@
<form string="Import module">
<field name="state" invisible="1"/>
<separator string="Import Module" colspan="4"/>
+ <p class="alert alert-warning">Note: you can only import data modules (.xml files and static assets)</p>
<group states="init" col="4">
<label for="module_file" string="Select module package to import (.zip file):" colspan="4"/>
<field name="module_file" colspan="4"/>
diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml
index 17f8dc6eee5..d14646f26c3 100644
--- a/addons/crm/views/crm_lead_views.xml
+++ b/addons/crm/views/crm_lead_views.xml
@@ -947,7 +947,6 @@ for rec in records:
<field name="domain">[('type','=','opportunity')]</field>
<field name="context">{
'default_type': 'opportunity',
- 'default_user_id': uid
}
</field>
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
@@ -1009,7 +1008,6 @@ for rec in records:
<field name="domain">[('type','=','opportunity')]</field>
<field name="context">{
'default_type': 'opportunity',
- 'default_user_id': uid,
'search_default_assigned_to_me': 1
}</field>
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py
index 8c4ee04489c..3fd6042153d 100644
--- a/addons/google_calendar/models/google_calendar.py
+++ b/addons/google_calendar/models/google_calendar.py
@@ -442,7 +442,9 @@ class GoogleCalendar(models.AbstractModel):
if google_attendee.get('found'):
continue
- attendee = ResPartner.search([('email', '=', google_attendee['email'])], limit=1)
+ attendee = ResPartner.search([('email', '=ilike', google_attendee['email']), ('user_ids', '!=', False)], limit=1)
+ if not attendee:
+ attendee = ResPartner.search([('email', '=ilike', google_attendee['email'])], limit=1)
if not attendee:
data = {
'email': partner_email,
diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py
index 31ea964d22f..318505f7108 100644
--- a/addons/hr_holidays/models/hr_leave_type.py
+++ b/addons/hr_holidays/models/hr_leave_type.py
@@ -247,7 +247,10 @@ class HolidaysType(models.Model):
if record.allocation_type != 'no':
name = "%(name)s (%(count)s)" % {
'name': name,
- 'count': _('%g remaining out of %g') % (float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0, float_round(record.max_leaves, precision_digits=2) or 0.0)
+ 'count': _('%g remaining out of %g') % (
+ float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0,
+ float_round(record.max_leaves, precision_digits=2) or 0.0,
+ )
}
res.append((record.id, name))
return res
diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py
index f5547e8cd66..6fd6fb73790 100644
--- a/addons/im_livechat/models/im_livechat_channel.py
+++ b/addons/im_livechat/models/im_livechat_channel.py
@@ -177,6 +177,7 @@ class ImLivechatChannel(models.Model):
'public': 'private',
'email_send': False,
})
+ mail_channel._broadcast([operator_partner_id])
return mail_channel.sudo().with_context(im_livechat_operator_partner_id=operator_partner_id).channel_info()[0]
@api.model
diff --git a/addons/l10n_it/__manifest__.py b/addons/l10n_it/__manifest__.py
index f78432b06b3..fd621d872fd 100644
--- a/addons/l10n_it/__manifest__.py
+++ b/addons/l10n_it/__manifest__.py
@@ -17,7 +17,7 @@ Piano dei conti italiano di un'impresa generica.
Italian accounting chart and localization.
""",
'category': 'Localization',
- 'website': 'http://www.openerp-italia.org/',
+ 'website': 'http://www.odoo.com/',
'data': [
'data/l10n_it_chart_data.xml',
'data/account.account.template.csv',
diff --git a/addons/l10n_mx/data/account_data.xml b/addons/l10n_mx/data/account_data.xml
index 782597ee8ea..8176c32b5f3 100644
--- a/addons/l10n_mx/data/account_data.xml
+++ b/addons/l10n_mx/data/account_data.xml
@@ -11,6 +11,10 @@
<field name="name">IVA 16% </field>
</record>
+ <record id="tax_group_iva_8" model="account.tax.group">
+ <field name="name">IVA 8%</field>
+ </record>
+
<record id="tax_group_iva_ret_4" model="account.tax.group">
<field name="name">IVA Retencion 4%</field>
</record>
diff --git a/addons/l10n_mx/data/account_tax_data.xml b/addons/l10n_mx/data/account_tax_data.xml
index adda588dcbd..d8b5cc4ecc3 100644
--- a/addons/l10n_mx/data/account_tax_data.xml
+++ b/addons/l10n_mx/data/account_tax_data.xml
@@ -30,6 +30,10 @@
<field name="name">DIOT: 0%</field>
<field name="applicability">taxes</field>
</record>
+ <record id="tag_diot_8" model="account.account.tag">
+ <field name="name">DIOT: 8%</field>
+ <field name="applicability">taxes</field>
+ </record>
<record id="tag_diot_ret" model="account.account.tag">
<field name="name">DIOT: Retención</field>
<field name="applicability">taxes</field>
@@ -190,5 +194,35 @@
<field name="cash_basis_account_id" ref="cuenta118_01"/>
<field name="tag_ids" eval="[(6,0,[ref('tag_diot_16')])]"/>
</record>
+
+ <record id="tax16" model="account.tax.template">
+ <field name="chart_template_id" ref="mx_coa"/>
+ <field name="name">IVA(8%) COMPRAS</field>
+ <field name="description">IVA(8%)</field>
+ <field name="amount">8</field>
+ <field name="amount_type">percent</field>
+ <field name="type_tax_use">purchase</field>
+ <field name="account_id" ref="cuenta119_01"/>
+ <field name="refund_account_id" ref="cuenta119_01"/>
+ <field name="tax_group_id" ref="tax_group_iva_8"/>
+ <field name="tax_exigibility">on_payment</field>
+ <field name="cash_basis_account" ref="cuenta118_01"/>
+ <field name="tag_ids" eval="[(6,0,[ref('tag_diot_8')])]"/>
+ </record>
+
+ <record id="tax17" model="account.tax.template">
+ <field name="chart_template_id" ref="mx_coa"/>
+ <field name="name">IVA(8%) VENTAS</field>
+ <field name="description">IVA(8%)</field>
+ <field name="amount">8</field>
+ <field name="amount_type">percent</field>
+ <field name="type_tax_use">sale</field>
+ <field name="account_id" ref="cuenta209_01"/>
+ <field name="refund_account_id" ref="cuenta209_01"/>
+ <field name="tag_ids" eval="[(6,0,[ref('tag_iva')])]"/>
+ <field name="tax_group_id" ref="tax_group_iva_8"/>
+ <field name="tax_exigibility">on_payment</field>
+ <field name="cash_basis_account" ref="cuenta208_01"/>
+ </record>
</data>
</odoo>
diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py
index 22aefea4cc7..36643c35c8f 100644
--- a/addons/mass_mailing/models/mass_mailing.py
+++ b/addons/mass_mailing/models/mass_mailing.py
@@ -715,6 +715,12 @@ class MassMailing(models.Model):
failed_mails = self.env['mail.mail'].search([('mailing_id', 'in', self.ids), ('state', '=', 'exception')])
failed_mails.mapped('statistics_ids').unlink()
failed_mails.sudo().unlink()
+ res_ids = self.get_recipients()
+ except_mailed = self.env['mail.mail.statistics'].search([
+ ('model', '=', self.mailing_model_real),
+ ('res_id', 'in', res_ids),
+ ('exception', '!=', False),
+ ('mass_mailing_id', '=', self.id)]).unlink()
self.write({'state': 'in_queue'})
def action_view_sent(self):
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 471c6f56cd7..97e53a919c8 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -492,13 +492,11 @@ class PosOrder(models.Model):
aml = order.statement_ids.mapped('journal_entry_ids') | order.account_move.line_ids | order.invoice_id.move_id.line_ids
aml = aml.filtered(lambda r: not r.reconciled and r.account_id.internal_type == 'receivable' and r.partner_id == order.partner_id.commercial_partner_id)
- # Reconcile returns first
- # to avoid mixing up the credit of a payment and the credit of a return
- # in the receivable account
- aml_returns = aml.filtered(lambda l: (l.journal_id.type == 'sale' and l.credit) or (l.journal_id.type != 'sale' and l.debit))
try:
- aml_returns.reconcile()
- (aml - aml_returns).reconcile()
+ # Cash returns will be well reconciled
+ # Whereas freight returns won't be
+ # "c'est la vie..."
+ aml.reconcile()
except Exception:
# There might be unexpected situations where the automatic reconciliation won't
# work. We don't want the user to be blocked because of this, since the automatic
diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js
index 7abb931d68e..b5cff2f233a 100644
--- a/addons/point_of_sale/static/src/js/gui.js
+++ b/addons/point_of_sale/static/src/js/gui.js
@@ -437,7 +437,7 @@ var Gui = core.Class.extend({
numpad_input: function(buffer, input, options) {
var newbuf = buffer.slice(0);
options = options || {};
- var newbuf_float = field_utils.parse.float(newbuf);
+ var newbuf_float = newbuf === '-' ? newbuf : field_utils.parse.float(newbuf);
var decimal_point = _t.database.parameters.decimal_point;
if (input === decimal_point) {
@@ -457,7 +457,9 @@ var Gui = core.Class.extend({
newbuf = newbuf.substring(1,newbuf.length);
}
} else if (input === '-') {
- if ( newbuf[0] === '-' ) {
+ if (options.firstinput) {
+ newbuf = '-';
+ } else if ( newbuf[0] === '-' ) {
newbuf = newbuf.substring(1,newbuf.length);
} else {
newbuf = '-' + newbuf;
diff --git a/addons/sale_crm/models/crm_lead.py b/addons/sale_crm/models/crm_lead.py
index 7982d7c3e4d..c01ef48d96a 100644
--- a/addons/sale_crm/models/crm_lead.py
+++ b/addons/sale_crm/models/crm_lead.py
@@ -20,7 +20,7 @@ class CrmLead(models.Model):
nbr = 0
company_currency = lead.company_currency or self.env.user.company_id.currency_id
for order in lead.order_ids:
- if order.state in ('draft', 'sent', 'sale'):
+ if order.state in ('draft', 'sent'):
nbr += 1
if order.state not in ('draft', 'sent', 'cancel'):
total += order.currency_id._convert(
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 2b3f9af2baa..1d133e2d95f 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -259,7 +259,9 @@ class Product(models.Model):
# TODO: Still optimization possible when searching virtual quantities
ids = []
- for product in self.with_context(prefetch_fields=False).search([]):
+ # Order the search on `id` to prevent the default order on the product name which slows
+ # down the search because of the join on the translation table to get the translated names.
+ for product in self.with_context(prefetch_fields=False).search([], order='id'):
if OPERATORS[operator](product[field], value):
ids.append(product.id)
return [('id', 'in', ids)]
diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml
index a91a860dee9..9a5552cf3fd 100644
--- a/addons/stock/report/report_deliveryslip.xml
+++ b/addons/stock/report/report_deliveryslip.xml
@@ -5,18 +5,14 @@
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang':o.partner_id.lang})" />
- <t t-if="o.move_lines and o.move_lines[0].partner_id" name="partner_header">
+ <t t-set="partner" t-value="o.partner_id or (o.move_lines and o.move_lines[0].partner_id) or False"/>
+ <t t-if="partner" name="partner_header">
<t t-set="address">
- <div t-field="o.move_lines[0].partner_id"
+ <div t-esc="parent"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/>
</t>
</t>
- <t t-if="not (o.move_lines and o.move_lines[0].partner_id) and o.partner_id" name="partner_header">
- <t t-set="address">
- <div t-field="o.partner_id"
- t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'/>
- </t>
- </t>
+
<div class="page">
<h2>
<span t-field="o.name"/>
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index e46c57c6a21..fbb951775dd 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -247,12 +247,9 @@
<group>
<group>
<field name="partner_id"/>
- <button name="action_assign_partner" string="Assign Partner" type="object" help="Assign the partner to the Operations. Use only if the partner is manually changed!"
- attrs="{'invisible': ['|', '|', ('move_line_exist', '=', False), ('state', 'not in', ('draft', 'assigned', 'confirmed')), ('picking_type_code', '=', 'internal')]}"
- class="oe_link oe_edit_only"/>
+ <field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
+ <field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
<field name="picking_type_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
- <field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
- <field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}"/>
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
</group>
<group>
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 9027a2ac792..3f195c1ea24 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -443,8 +443,10 @@ var PivotModel = AbstractModel.extend({
self._updateMainGroupBys(old_row, self.data.main_row);
}
- self._updateTree(old_col.root, self.data.main_col.root);
- self._updateMainGroupBys(old_col, self.data.main_col);
+ if (!('pivot_column_groupby' in (params.context || {}))) {
+ self._updateTree(old_col.root, self.data.main_col.root);
+ self._updateMainGroupBys(old_col, self.data.main_col);
+ }
});
},
/**
diff --git a/addons/web/static/src/js/views/search/search_filters.js b/addons/web/static/src/js/views/search/search_filters.js
index 86fdff9f0cb..8aa13db8aaf 100644
--- a/addons/web/static/src/js/views/search/search_filters.js
+++ b/addons/web/static/src/js/views/search/search_filters.js
@@ -243,7 +243,7 @@ var DateTime = Field.extend({
_create_new_widget: function (name) {
this[name] = new (this._get_widget_class())(this);
return this[name].appendTo(this.$el).then((function () {
- this[name].setValue(moment(new Date()));
+ this[name].setValue(moment());
}).bind(this));
},
_get_widget_class: function () {
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index 545cf09b734..b09f4d5b1bf 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -987,6 +987,95 @@ QUnit.module('Views', {
pivot.destroy();
});
+ QUnit.test('Unload Filter, reset display, load another filter', function (assert) {
+ assert.expect(18);
+
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1,
+ 'The column should be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1,
+ 'There should be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1,
+ 'There should be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1,
+ 'The row should be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1,
+ 'There should be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1,
+ 'There should be a row "xpad"');
+
+ // Equivalent to unload the filter
+ var reloadParams = {
+ context: {},
+ };
+ pivot.reload(reloadParams);
+ // collapse all headers
+ pivot.$('.o_pivot_header_cell_opened').click();
+ pivot.$('.o_pivot_header_cell_opened').click();
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed').length, 1,
+ 'The column should not be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 0,
+ 'There should not be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 0,
+ 'There should not be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed').length, 1,
+ 'The row should not be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 0,
+ 'There should not be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 0,
+ 'There should not be a row "xpad"');
+
+ // Equivalent to load another filter
+ reloadParams = {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ };
+ pivot.reload(reloadParams);
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1,
+ 'The column should be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1,
+ 'There should be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1,
+ 'There should be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1,
+ 'The row should be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1,
+ 'There should be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1,
+ 'There should be a row "xpad"');
+
+ pivot.destroy();
+ });
+
QUnit.test('correctly uses pivot_ keys from the context', function (assert) {
assert.expect(7);
diff --git a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
index 39581ee54c8..28a50c96c5e 100644
--- a/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
+++ b/addons/website_sale_delivery/static/src/js/website_sale_delivery.js
@@ -1,10 +1,12 @@
-'use strict';
odoo.define('website_sale_delivery.checkout', function (require) {
+ 'use strict';
require('web.dom_ready');
var ajax = require('web.ajax');
var core = require('web.core');
var _t = core._t;
+ var concurrency = require('web.concurrency');
+ var dp = new concurrency.DropPrevious();
/* Handle interactive carrier choice + cart update */
var $pay_button = $('#o_payment_form_pay');
@@ -54,7 +56,7 @@ odoo.define('website_sale_delivery.checkout', function (require) {
$pay_button.prop('disabled', true);
var carrier_id = $(ev.currentTarget).val();
var values = {'carrier_id': carrier_id};
- ajax.jsonRpc('/shop/update_carrier', 'call', values)
+ dp.add(ajax.jsonRpc('/shop/update_carrier', 'call', values))
.then(_onCarrierUpdateAnswer);
};