summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tx/config15
-rw-r--r--addons/account/models/account_bank_statement.py2
-rw-r--r--addons/account/models/account_invoice.py15
-rw-r--r--addons/account/models/company.py10
-rw-r--r--addons/account/report/account_aged_partner_balance.py2
-rw-r--r--addons/account/report/account_balance.py2
-rw-r--r--addons/account/report/account_general_ledger.py2
-rw-r--r--addons/account/report/account_journal.py2
-rw-r--r--addons/account/report/account_partner_ledger.py2
-rw-r--r--addons/account/report/account_report_financial.py2
-rw-r--r--addons/account/views/web_planner_data.xml2
-rw-r--r--addons/account_bank_statement_import/account_bank_statement_import.py2
-rw-r--r--addons/hr_expense/data/hr_expense_data.xml2
-rw-r--r--addons/hr_expense/models/hr_expense.py30
-rw-r--r--addons/hr_expense/views/hr_expense_views.xml2
-rw-r--r--addons/hr_holidays/report/holidays_summary_report.py2
-rw-r--r--addons/hr_payroll/report/report_contribution_register.py2
-rw-r--r--addons/l10n_fr/data/account_chart_template_data.xml4
-rw-r--r--addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py2
-rw-r--r--addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py2
-rw-r--r--addons/mass_mailing/models/mail_mail.py7
-rw-r--r--addons/mass_mailing/models/mail_thread.py10
-rw-r--r--addons/mrp_repair/models/mrp_repair.py11
-rw-r--r--addons/payment_authorize/models/payment.py4
-rw-r--r--addons/point_of_sale/models/pos_order.py2
-rw-r--r--addons/sale/models/sale_analytic.py15
-rw-r--r--addons/sale/wizard/sale_make_invoice_advance.py1
-rw-r--r--addons/sale_timesheet/models/sale_order.py15
-rw-r--r--addons/stock/models/product.py6
-rw-r--r--addons/website_quote/models/sale_quote.py2
-rw-r--r--addons/website_quote/static/src/js/website_quotation.js6
-rw-r--r--addons/website_sale/views/templates.xml2
-rw-r--r--doc/cla/corporate/tawasta.md14
-rw-r--r--doc/cla/individual/sindhav.md10
-rw-r--r--doc/cla/individual/timotalvitie.md11
-rw-r--r--doc/howtos/backend/exercise-calendar2
-rw-r--r--doc/howtos/backend/exercise-kanban2
-rw-r--r--doc/reference/module.rst4
38 files changed, 154 insertions, 74 deletions
diff --git a/.tx/config b/.tx/config
index 624149f2f75..c6806928d84 100644
--- a/.tx/config
+++ b/.tx/config
@@ -142,6 +142,11 @@ file_filter = addons/base_import_module/i18n/<lang>.po
source_file = addons/base_import_module/i18n/base_import_module.pot
source_lang = en
+[odoo-master.base_sparse_field]
+file_filter = addons/base_sparse_field/i18n/<lang>.po
+source_file = addons/base_sparse_field/i18n/base_sparse_field.pot
+source_lang = en
+
[odoo-master.base_setup]
file_filter = addons/base_setup/i18n/<lang>.po
source_file = addons/base_setup/i18n/base_setup.pot
@@ -897,6 +902,11 @@ file_filter = addons/website_sale/i18n/<lang>.po
source_file = addons/website_sale/i18n/website_sale.pot
source_lang = en
+[odoo-master.website_sale_comparison]
+file_filter = addons/website_sale_comparison/i18n/<lang>.po
+source_file = addons/website_sale_comparison/i18n/website_sale_comparison.pot
+source_lang = en
+
[odoo-master.website_sale_delivery]
file_filter = addons/website_sale_delivery/i18n/<lang>.po
source_file = addons/website_sale_delivery/i18n/website_sale_delivery.pot
@@ -917,6 +927,11 @@ file_filter = addons/website_sale_stock/i18n/<lang>.po
source_file = addons/website_sale_stock/i18n/website_sale_stock.pot
source_lang = en
+[odoo-master.website_sale_wishlist]
+file_filter = addons/website_sale_wishlist/i18n/<lang>.po
+source_file = addons/website_sale_wishlist/i18n/website_sale_wishlist.pot
+source_lang = en
+
[odoo-master.website_slides]
file_filter = addons/website_slides/i18n/<lang>.po
source_file = addons/website_slides/i18n/website_slides.pot
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index dc1678c92e9..56d3c59fd9f 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -910,7 +910,7 @@ class AccountBankStatementLine(models.Model):
counterpart_moves = (counterpart_moves | move)
# Create The payment
- payment = False
+ payment = self.env['account.payment']
if abs(total)>0.00001:
partner_id = self.partner_id and self.partner_id.id or False
partner_type = False
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 52b1dbf7f1a..59371686f1a 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -714,7 +714,7 @@ class AccountInvoice(models.Model):
total_currency = 0
for line in invoice_move_lines:
if self.currency_id != company_currency:
- currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
+ currency = self.currency_id.with_context(date=self.date or self.date_invoice or fields.Date.context_today(self))
if not (line.get('currency_id') and line.get('amount_currency')):
line['currency_id'] = currency.id
line['amount_currency'] = currency.round(line['price'])
@@ -848,7 +848,6 @@ class AccountInvoice(models.Model):
if not inv.date_invoice:
inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)})
- date_invoice = inv.date_invoice
company_currency = inv.company_id.currency_id
# create move lines (one per invoice line + eventual taxes and analytic lines)
@@ -861,9 +860,9 @@ class AccountInvoice(models.Model):
name = inv.name or '/'
if inv.payment_term_id:
- totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, date_invoice)[0]
+ totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
res_amount_currency = total_currency
- ctx['date'] = date_invoice
+ ctx['date'] = inv.date or inv.date_invoice
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id)
@@ -903,7 +902,7 @@ class AccountInvoice(models.Model):
journal = inv.journal_id.with_context(ctx)
line = inv.finalize_invoice_move_lines(line)
- date = inv.date or date_invoice
+ date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
'line_ids': line,
@@ -1504,6 +1503,12 @@ class AccountPaymentTerm(models.Model):
result.append((last_date, dist))
return result
+ @api.multi
+ def unlink(self):
+ property_recs = self.env['ir.property'].search([('value_reference', 'in', ['account.payment.term,%s'%payment_term.id for payment_term in self])])
+ property_recs.unlink()
+ return super(AccountPaymentTerm, self).unlink()
+
class AccountPaymentTermLine(models.Model):
_name = "account.payment.term.line"
diff --git a/addons/account/models/company.py b/addons/account/models/company.py
index 44eee5432ae..584c1e44c99 100644
--- a/addons/account/models/company.py
+++ b/addons/account/models/company.py
@@ -57,10 +57,16 @@ Best Regards,''')
if (date.month < last_month or (date.month == last_month and date.day <= last_day)):
date = date.replace(month=last_month, day=last_day)
else:
- date = date.replace(month=last_month, day=last_day, year=date.year + 1)
+ if last_month == 2 and last_day == 29 and (date.year + 1) % 4 != 0:
+ date = date.replace(month=last_month, day=28, year=date.year + 1)
+ else:
+ date = date.replace(month=last_month, day=last_day, year=date.year + 1)
date_to = date
date_from = date + timedelta(days=1)
- date_from = date_from.replace(year=date_from.year - 1)
+ if date_from.month == 2 and date_from.day == 29:
+ date_from = date_from.replace(day=28, year=date_from.year - 1)
+ else:
+ date_from = date_from.replace(year=date_from.year - 1)
return {'date_from': date_from, 'date_to': date_to}
def get_new_account_code(self, current_code, old_prefix, new_prefix, digits):
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index 9ff333ec24b..5fc6d3ece67 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -198,7 +198,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
total = []
model = self.env.context.get('active_model')
diff --git a/addons/account/report/account_balance.py b/addons/account/report/account_balance.py
index fccc60e7680..5e7c441a671 100644
--- a/addons/account/report/account_balance.py
+++ b/addons/account/report/account_balance.py
@@ -61,7 +61,7 @@ class ReportTrialBalance(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
self.model = self.env.context.get('active_model')
docs = self.env[self.model].browse(self.env.context.get('active_ids', []))
diff --git a/addons/account/report/account_general_ledger.py b/addons/account/report/account_general_ledger.py
index f7ebf8ad451..e154b629fcd 100644
--- a/addons/account/report/account_general_ledger.py
+++ b/addons/account/report/account_general_ledger.py
@@ -111,7 +111,7 @@ class ReportGeneralLedger(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
self.model = self.env.context.get('active_model')
docs = self.env[self.model].browse(self.env.context.get('active_ids', []))
diff --git a/addons/account/report/account_journal.py b/addons/account/report/account_journal.py
index 9dfa7807f48..9a26cdeb397 100644
--- a/addons/account/report/account_journal.py
+++ b/addons/account/report/account_journal.py
@@ -97,7 +97,7 @@ class ReportJournal(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
target_move = data['form'].get('target_move', 'all')
sort_selection = data['form'].get('sort_selection', 'date')
diff --git a/addons/account/report/account_partner_ledger.py b/addons/account/report/account_partner_ledger.py
index 94d12a7bd3a..4787469f476 100644
--- a/addons/account/report/account_partner_ledger.py
+++ b/addons/account/report/account_partner_ledger.py
@@ -71,7 +71,7 @@ class ReportPartnerLedger(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
data['computed'] = {}
diff --git a/addons/account/report/account_report_financial.py b/addons/account/report/account_report_financial.py
index 4aacdf68108..918919014bf 100644
--- a/addons/account/report/account_report_financial.py
+++ b/addons/account/report/account_report_financial.py
@@ -146,7 +146,7 @@ class ReportFinancial(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
self.model = self.env.context.get('active_model')
docs = self.env[self.model].browse(self.env.context.get('active_id'))
diff --git a/addons/account/views/web_planner_data.xml b/addons/account/views/web_planner_data.xml
index abd637d87d5..f040f3482b9 100644
--- a/addons/account/views/web_planner_data.xml
+++ b/addons/account/views/web_planner_data.xml
@@ -200,7 +200,7 @@
</p>
<div class="alert alert-info info_icon" role="alert">
<span class="fa fa-lightbulb-o fa-lg"/>
- <p>Exchange rates can be automatically updated once a day from <strong>Yahoo Finance</strong> or the <strong>European Central Bank</strong>. You can activate this feature in the bottom of the <a t-att-href="prepare_backend_url('account.action_account_config')">Accounting Settings</a><a t-att-href="prepare_backend_url('account.action_account_config')">accounting settings</a>.</p>
+ <p>Exchange rates can be automatically updated once a day from <strong>Yahoo Finance</strong> or the <strong>European Central Bank</strong>. You can activate this feature in the bottom of the <a t-att-href="prepare_backend_url('account.action_account_config')">Accounting Settings</a>.</p>
</div>
</t>
<t t-call="web_planner.page">
diff --git a/addons/account_bank_statement_import/account_bank_statement_import.py b/addons/account_bank_statement_import/account_bank_statement_import.py
index 07ab4c7dc6e..9056b7af916 100644
--- a/addons/account_bank_statement_import/account_bank_statement_import.py
+++ b/addons/account_bank_statement_import/account_bank_statement_import.py
@@ -223,7 +223,7 @@ class AccountBankStatementImport(models.TransientModel):
else:
ignored_statement_lines_import_ids.append(line_vals['unique_import_id'])
if 'balance_start' in st_vals:
- st_vals['balance_start'] += line_vals['amount']
+ st_vals['balance_start'] += float(line_vals['amount'])
if len(filtered_st_lines) > 0:
# Remove values that won't be used to create records
diff --git a/addons/hr_expense/data/hr_expense_data.xml b/addons/hr_expense/data/hr_expense_data.xml
index b922a38d43c..100282658a8 100644
--- a/addons/hr_expense/data/hr_expense_data.xml
+++ b/addons/hr_expense/data/hr_expense_data.xml
@@ -44,7 +44,7 @@
<field name="name">Submitted</field>
<field name="res_model">hr.expense.sheet</field>
<field name="description">Expense report submitted, waiting approval</field>
- <field name="default" eval="False"/>
+ <field name="default" eval="True"/>
</record>
<record id="mt_expense_paid" model="mail.message.subtype">
<field name="name">Paid</field>
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index 5c03e56c495..efc1433b629 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -30,7 +30,8 @@ class HrExpense(models.Model):
company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account')
- account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=lambda self: self.env['ir.property'].get('property_account_expense_categ_id', 'product.category'))
+ account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=lambda self: self.env['ir.property'].get('property_account_expense_categ_id', 'product.category'),
+ help="An expense account is expected")
description = fields.Text()
payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
@@ -418,10 +419,15 @@ class HrExpenseSheet(models.Model):
@api.model
def create(self, vals):
+ # Add the followers at creation, so they can be notified
+ if vals.get('employee_id'):
+ employee = self.env['hr.employee'].browse(vals['employee_id'])
+ users = self._get_users_to_subscribe(employee=employee) - self.env.user
+ vals['message_follower_ids'] = []
+ for partner in users.mapped('partner_id'):
+ vals['message_follower_ids'] += self.env['mail.followers']._add_follower_command(self._name, [], {partner.id: None}, {})[0]
sheet = super(HrExpenseSheet, self).create(vals)
self.check_consistency()
- if vals.get('employee_id'):
- sheet._add_followers()
return sheet
@api.multi
@@ -456,16 +462,20 @@ class HrExpenseSheet(models.Model):
return 'hr_expense.mt_expense_paid'
return super(HrExpenseSheet, self)._track_subtype(init_values)
- def _add_followers(self):
- user_ids = []
- employee = self.employee_id
+ def _get_users_to_subscribe(self, employee=False):
+ users = self.env['res.users']
+ employee = employee or self.employee_id
if employee.user_id:
- user_ids.append(employee.user_id.id)
+ users |= employee.user_id
if employee.parent_id:
- user_ids.append(employee.parent_id.user_id.id)
+ users |= employee.parent_id.user_id
if employee.department_id and employee.department_id.manager_id and employee.parent_id != employee.department_id.manager_id:
- user_ids.append(employee.department_id.manager_id.user_id.id)
- self.message_subscribe_users(user_ids=user_ids)
+ users |= employee.department_id.manager_id.user_id
+ return users
+
+ def _add_followers(self):
+ users = self._get_users_to_subscribe()
+ self.message_subscribe_users(user_ids=users.ids)
@api.onchange('employee_id')
def _onchange_employee_id(self):
diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index ed78a21531e..7ede7d1efb7 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -60,7 +60,7 @@
</group><group>
<field name="reference"/>
<field name="date"/>
- <field name="account_id"/>
+ <field name="account_id" domain="[('internal_type', '=', 'other')]"/>
<field name="employee_id"/>
<field name="sheet_id" invisible="1"/>
<field name="currency_id" groups="base.group_multi_currency"/>
diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py
index 1428562ed62..42c447655b1 100644
--- a/addons/hr_holidays/report/holidays_summary_report.py
+++ b/addons/hr_holidays/report/holidays_summary_report.py
@@ -104,7 +104,7 @@ class HrHolidaySummaryReport(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
Report = self.env['report']
holidays_report = Report._get_report_from_name('hr_holidays.report_holidayssummary')
diff --git a/addons/hr_payroll/report/report_contribution_register.py b/addons/hr_payroll/report/report_contribution_register.py
index c56918c88be..3ae49b196ec 100644
--- a/addons/hr_payroll/report/report_contribution_register.py
+++ b/addons/hr_payroll/report/report_contribution_register.py
@@ -31,7 +31,7 @@ class ContributionRegisterReport(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
register_ids = self.env.context.get('active_ids', [])
contrib_registers = self.env['hr.contribution.register'].browse(register_ids)
diff --git a/addons/l10n_fr/data/account_chart_template_data.xml b/addons/l10n_fr/data/account_chart_template_data.xml
index 59fb761390d..aea92e3e02f 100644
--- a/addons/l10n_fr/data/account_chart_template_data.xml
+++ b/addons/l10n_fr/data/account_chart_template_data.xml
@@ -2928,6 +2928,7 @@
<field name="name">Coupons échus à l'encaissement</field>
<field name="code">5111</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
+ <field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2935,6 +2936,7 @@
<field name="name">Chèques à encaisser</field>
<field name="code">5112</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
+ <field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2942,6 +2944,7 @@
<field name="name">Effets à l'encaissement</field>
<field name="code">5113</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
+ <field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
@@ -2949,6 +2952,7 @@
<field name="name">Effets à l'escompte</field>
<field name="code">5114</field>
<field name="user_type_id" ref="account.data_account_type_liquidity"/>
+ <field name="reconcile" eval="True"/>
<field name="chart_template_id" ref="l10n_fr_pcg_chart_template"/>
</record>
diff --git a/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py
index 1cc67073146..f42b76eaea6 100644
--- a/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py
+++ b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py
@@ -95,7 +95,7 @@ class ReportHrSalaryEmployeeBymonth(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
diff --git a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py
index bb6e2c75423..dd768cf9f5f 100644
--- a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py
+++ b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py
@@ -129,7 +129,7 @@ class EmployeesYearlySalaryReport(models.AbstractModel):
@api.model
def render_html(self, docids, data=None):
if not self.env.context.get('active_model') or not self.env.context.get('active_id'):
- raise UserError(_("Some data are missing, this report cannot be printed."))
+ raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py
index 3a69530db1a..5c4f68b5597 100644
--- a/addons/mass_mailing/models/mail_mail.py
+++ b/addons/mass_mailing/models/mail_mail.py
@@ -61,15 +61,14 @@ class MailMail(models.Model):
self.ensure_one()
body = super(MailMail, self).send_get_mail_body(partner=partner)
- links_blacklist = ['/unsubscribe_from_list']
-
if self.mailing_id and body and self.statistics_ids:
for match in re.findall(URL_REGEX, self.body_html):
-
href = match[0]
url = match[1]
- if not [s for s in links_blacklist if s in href]:
+ parsed = urlparse.urlparse(url, scheme='http')
+
+ if parsed.scheme.startswith('http') and parsed.path.startswith('/r/'):
new_href = href.replace(url, url + '/m/' + str(self.statistics_ids[0].id))
body = body.replace(href, new_href)
diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py
index 8e515a664f8..ee5fb45f6eb 100644
--- a/addons/mass_mailing/models/mail_thread.py
+++ b/addons/mass_mailing/models/mail_thread.py
@@ -39,3 +39,13 @@ class MailThread(models.AbstractModel):
message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()]
self.env['mail.mail.statistics'].set_replied(mail_message_ids=message_ids)
return super(MailThread, self).message_route_process(message, message_dict, routes)
+
+ @api.multi
+ def message_post_with_template(self, template_id, **kwargs):
+ # avoid having message send through `message_post*` methods being implicitly considered as
+ # mass-mailing
+ no_massmail = self.with_context(
+ default_mass_mailing_name=False,
+ default_mass_mailing_id=False,
+ )
+ return super(MailThread, no_massmail).message_post_with_template(template_id, **kwargs)
diff --git a/addons/mrp_repair/models/mrp_repair.py b/addons/mrp_repair/models/mrp_repair.py
index 8de88aeabf2..446db09423c 100644
--- a/addons/mrp_repair/models/mrp_repair.py
+++ b/addons/mrp_repair/models/mrp_repair.py
@@ -249,11 +249,12 @@ class Repair(models.Model):
return self.env['report'].get_action(self, 'mrp_repair.report_mrprepairorder')
def action_repair_invoice_create(self):
- self.action_invoice_create()
- if self.invoice_method == 'b4repair':
- self.action_repair_ready()
- elif self.invoice_method == 'after_repair':
- self.write({'state': 'done'})
+ for repair in self:
+ repair.action_invoice_create()
+ if repair.invoice_method == 'b4repair':
+ repair.action_repair_ready()
+ elif repair.invoice_method == 'after_repair':
+ repair.write({'state': 'done'})
return True
@api.multi
diff --git a/addons/payment_authorize/models/payment.py b/addons/payment_authorize/models/payment.py
index b90b3f6d8f5..de9c74ae294 100644
--- a/addons/payment_authorize/models/payment.py
+++ b/addons/payment_authorize/models/payment.py
@@ -256,14 +256,14 @@ class TxAuthorize(models.Model):
def authorize_s2s_capture_transaction(self):
self.ensure_one()
transaction = AuthorizeAPI(self.acquirer_id)
- tree = transaction.capture(self.acquirer_reference, self.amount)
+ tree = transaction.capture(self.acquirer_reference or '', self.amount)
return self._authorize_s2s_validate_tree(tree)
@api.multi
def authorize_s2s_void_transaction(self):
self.ensure_one()
transaction = AuthorizeAPI(self.acquirer_id)
- tree = transaction.void(self.acquirer_reference)
+ tree = transaction.void(self.acquirer_reference or '')
return self._authorize_s2s_validate_tree(tree)
@api.multi
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index f52e65fed77..adc633fccc0 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -627,7 +627,7 @@ class PosOrder(models.Model):
return_picking = Picking.create(return_vals)
return_picking.message_post(body=message)
- for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu'] and l.qty != 0):
+ for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu'] and not float_is_zero(l.qty, precision_digits=l.product_id.uom_id.rounding)):
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
diff --git a/addons/sale/models/sale_analytic.py b/addons/sale/models/sale_analytic.py
index ef58a8e44aa..5849845e660 100644
--- a/addons/sale/models/sale_analytic.py
+++ b/addons/sale/models/sale_analytic.py
@@ -10,27 +10,20 @@ class SaleOrderLine(models.Model):
@api.multi
def _compute_analytic(self, domain=None):
lines = {}
+ force_so_lines = self.env.context.get("force_so_lines")
if not domain:
+ if not self.ids and not force_so_lines:
+ return True
# To filter on analyic lines linked to an expense
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
expense_type_id = expense_type_id and expense_type_id.id
- domain = [
- ('so_line', 'in', self.ids),
- '|',
- ('amount', '<', 0),
- '&',
- ('amount', '=', 0),
- '|',
- ('move_id', '=', False),
- ('move_id.account_id.user_type_id', '=', expense_type_id)
- ]
+ domain = [('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
data = self.env['account.analytic.line'].read_group(
domain,
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
)
# If the unlinked analytic line was the last one on the SO line, the qty was not updated.
- force_so_lines = self.env.context.get("force_so_lines")
if force_so_lines:
for line in force_so_lines:
lines.setdefault(line, 0.0)
diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py
index 886d80d19bd..2b9caf659bb 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.py
+++ b/addons/sale/wizard/sale_make_invoice_advance.py
@@ -113,6 +113,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
'payment_term_id': order.payment_term_id.id,
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
'team_id': order.team_id.id,
+ 'user_id': order.user_id.id,
'comment': order.note,
})
invoice.compute_taxes()
diff --git a/addons/sale_timesheet/models/sale_order.py b/addons/sale_timesheet/models/sale_order.py
index 0254c3dbe0e..0db9b0c0632 100644
--- a/addons/sale_timesheet/models/sale_order.py
+++ b/addons/sale_timesheet/models/sale_order.py
@@ -140,20 +140,9 @@ class SaleOrderLine(models.Model):
@api.multi
def _compute_analytic(self, domain=None):
- if not domain:
+ if not domain and self.ids:
# To filter on analyic lines linked to an expense
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
expense_type_id = expense_type_id and expense_type_id.id
- domain = [
- ('so_line', 'in', self.ids),
- '|',
- '|',
- ('amount', '<', 0.0),
- ('project_id', '!=', False),
- '&',
- ('amount', '=', 0),
- '|',
- ('move_id', '=', False),
- ('move_id.account_id.user_type_id', '=', expense_type_id)
- ]
+ domain = [('so_line', 'in', self.ids), '|', ('amount', '<=', 0.0), ('project_id', '!=', False)]
return super(SaleOrderLine, self)._compute_analytic(domain=domain)
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 54b4a877fd7..ae0fa43c55b 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -365,6 +365,12 @@ class Product(models.Model):
def action_view_routes(self):
return self.mapped('product_tmpl_id').action_view_routes()
+ @api.multi
+ def write(self, values):
+ res = super(Product, self).write(values)
+ if 'active' in values and not values['active'] and self.mapped('orderpoint_ids').filtered(lambda r: r.active):
+ raise UserError(_('You still have some active reordering rules on this product. Please archive or delete them first.'))
+ return res
class ProductTemplate(models.Model):
_inherit = 'product.template'
diff --git a/addons/website_quote/models/sale_quote.py b/addons/website_quote/models/sale_quote.py
index 79743fdc814..19b944df129 100644
--- a/addons/website_quote/models/sale_quote.py
+++ b/addons/website_quote/models/sale_quote.py
@@ -24,7 +24,7 @@ class SaleQuoteTemplate(models.Model):
], 'Payment', help="Require immediate payment by the customer when validating the order from the website quote")
mail_template_id = fields.Many2one('mail.template', 'Confirmation Mail',
help="This e-mail template will be sent on confirmation. Leave empty to send nothing.",
- domain=[('model', '=', 'sale.quote.template')])
+ domain=[('model', '=', 'sale.order')])
@api.multi
def open_template(self):
diff --git a/addons/website_quote/static/src/js/website_quotation.js b/addons/website_quote/static/src/js/website_quotation.js
index a6e1b701c5e..04705686f32 100644
--- a/addons/website_quote/static/src/js/website_quotation.js
+++ b/addons/website_quote/static/src/js/website_quotation.js
@@ -135,12 +135,18 @@ if(!$('.o_website_quote').length) {
case "h1":
var id = self.setElementId('quote_header_', el);
var text = self.extractText($(el));
+ if (!text) {
+ break;
+ }
last_li = $("<li>").append($('<a href="#'+id+'"/>').text(text)).appendTo(self.$el);
last_ul = false;
break;
case "h2":
var id = self.setElementId('quote_', el);
var text = self.extractText($(el));
+ if (!text) {
+ break;
+ }
if (last_li) {
if (!last_ul) {
last_ul = $("<ul class='nav'>").appendTo(last_li);
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index b742cf5f235..8197691fae3 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -170,7 +170,7 @@
<div class="products_pager">
<div class="row" style="width: 100%;">
<t t-call="website_sale.search">
- <t t-set="search_class">pagination form-inline o_website_sale_search</t>
+ <t t-set="search_class" t-value="'pagination form-inline o_website_sale_search'"/>
</t>
<t t-call="website_sale.pricelist_list" />
<t t-call="website.pager" />
diff --git a/doc/cla/corporate/tawasta.md b/doc/cla/corporate/tawasta.md
new file mode 100644
index 00000000000..130e6adf0f2
--- /dev/null
+++ b/doc/cla/corporate/tawasta.md
@@ -0,0 +1,14 @@
+Finland, 2017-07-17
+
+Oy Tawasta OS Technologies Ltd. agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0.
+
+We declare that we are authorized and able to make this agreement and sign this declaration.
+
+Signed,
+
+ Jarmo Kortetjärvi jarmo.kortetjarvi@tawasta.fi https://github.com/jarmokortetjarvi
+
+List of contributors:
+
+ Aleksi Savijoki aleksi.savijoki@tawasta.fi https://github.com/savijoki
+ Jarmo Kortetjärvi jarmo.kortetjarvi@tawasta.fi https://github.com/jarmokortetjarvi
diff --git a/doc/cla/individual/sindhav.md b/doc/cla/individual/sindhav.md
new file mode 100644
index 00000000000..876d54b2475
--- /dev/null
+++ b/doc/cla/individual/sindhav.md
@@ -0,0 +1,10 @@
+India, 2017-07-04
+
+I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this declaration.
+
+Signed,
+
+Rakesh Sindhav sindhav21591@gmail.com https://github.com/sindhav
+
diff --git a/doc/cla/individual/timotalvitie.md b/doc/cla/individual/timotalvitie.md
new file mode 100644
index 00000000000..1268e38225e
--- /dev/null
+++ b/doc/cla/individual/timotalvitie.md
@@ -0,0 +1,11 @@
+Finland, 2017-07-19
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Timo Talvitie timo.talvitie@vizucom.com https://github.com/timotalvitie
diff --git a/doc/howtos/backend/exercise-calendar b/doc/howtos/backend/exercise-calendar
index 60774310884..f63422c95a1 100644
--- a/doc/howtos/backend/exercise-calendar
+++ b/doc/howtos/backend/exercise-calendar
@@ -11,7 +11,7 @@ diff --git a/openacademy/models.py b/openacademy/models.py
+from datetime import timedelta
from odoo import models, fields, api, exceptions
- class Session(models.Model):
+ class Course(models.Model):
@@ -55,6 +56,8 @@ class Session(models.Model):
attendee_ids = fields.Many2many('res.partner', string="Attendees")
diff --git a/doc/howtos/backend/exercise-kanban b/doc/howtos/backend/exercise-kanban
index 68001ea87a8..14899f46a3d 100644
--- a/doc/howtos/backend/exercise-kanban
+++ b/doc/howtos/backend/exercise-kanban
@@ -17,7 +17,7 @@ Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:21:58.627015606 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:21:58.623015606 +0200
-@@ -169,11 +169,57 @@
+@@ -168,11 +168,57 @@
</field>
</record>
diff --git a/doc/reference/module.rst b/doc/reference/module.rst
index 76200fbc9a1..aecb8de8a8f 100644
--- a/doc/reference/module.rst
+++ b/doc/reference/module.rst
@@ -30,11 +30,11 @@ dictionary, where each key specifies module metadatum.
""",
# data files always loaded at installation
'data': [
- 'mymodule_view.xml',
+ 'views/mymodule_view.xml',
],
# data files containing optionally loaded demonstration data
'demo': [
- 'demo_data.xml',
+ 'demo/demo_data.xml',
],
}