diff options
| author | Christophe Simonis <chs@odoo.com> | 2017-10-26 19:03:19 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2017-10-26 19:03:19 +0200 |
| commit | a2890c1e76c9c73d80689f78aa2e6665ecdc9f31 (patch) | |
| tree | 9eb71d9b266535bb9a789a0c9e5692a4431993ce /addons | |
| parent | f86141aac265bb972e5dd6688f7bde674f4d1249 (diff) | |
| parent | bbc226cd6cc17c69979f81831a3da8cc668bf064 (diff) | |
[MERGE] forward port branch 10.0 up to bbc226cd6cc
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/models/account_move.py | 14 | ||||
| -rw-r--r-- | addons/google_calendar/models/google_calendar.py | 2 | ||||
| -rw-r--r-- | addons/hr_expense/models/hr_expense.py | 22 | ||||
| -rw-r--r-- | addons/l10n_ca/data/account_chart_template_after_data.xml | 2 | ||||
| -rw-r--r-- | addons/l10n_ca/data/l10n_ca_chart_data.xml | 2 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/wizard/account_fr_fec.py | 14 | ||||
| -rw-r--r-- | addons/mrp/models/stock_move.py | 2 | ||||
| -rw-r--r-- | addons/sale/models/sale.py | 18 | ||||
| -rw-r--r-- | addons/website/static/src/js/website.js | 2 | ||||
| -rw-r--r-- | addons/website_event/static/src/js/website_event.js | 4 | ||||
| -rw-r--r-- | addons/website_event/views/website_event_templates.xml | 4 | ||||
| -rw-r--r-- | addons/website_event_questions/views/website_event_questions_templates.xml | 2 | ||||
| -rw-r--r-- | addons/website_mail_channel/controllers/main.py | 14 | ||||
| -rw-r--r-- | addons/website_mail_channel/tests/__init__.py | 2 | ||||
| -rw-r--r-- | addons/website_mail_channel/tests/test_unsubscribe.py | 71 |
15 files changed, 137 insertions, 38 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index f724ef8dcd3..79c539dcacf 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1533,6 +1533,15 @@ class AccountPartialReconcile(models.Model): partial_reconciles._compute_partial_lines() return move_lines, partial_reconciles + def _get_tax_cash_basis_base_account(self, line, tax): + ''' Get the account of lines that will contain the base amount of taxes. + + :param line: An account.move.line record + :param tax: An account.tax record + :return: An account record + ''' + return line.account_id + def create_tax_cash_basis_entry(self, percentage_before_rec): self.ensure_one() move_date = self.debit_move_id.date @@ -1590,11 +1599,12 @@ class AccountPartialReconcile(models.Model): newly_created_move = self._create_tax_basis_move() #create cash basis entry for the base for tax in line.tax_ids: + 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': line.account_id.id, + 'account_id': account_id.id, 'tax_exigible': True, 'tax_ids': [(6, 0, [tax.id])], 'move_id': newly_created_move.id, @@ -1606,7 +1616,7 @@ class AccountPartialReconcile(models.Model): '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': line.account_id.id, + 'account_id': account_id.id, 'tax_exigible': True, 'move_id': newly_created_move.id, 'currency_id': line.currency_id.id, diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index 710b0ddaab9..5caeaf87e89 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -922,7 +922,7 @@ class GoogleCalendar(models.AbstractModel): def get_minTime(self): number_of_week = self.env['ir.config_parameter'].sudo().get_param('calendar.week_synchro', default=13) - return datetime.now() - timedelta(weeks=number_of_week) + return datetime.now() - timedelta(weeks=int(number_of_week)) def get_need_synchro_attendee(self): return self.env['ir.config_parameter'].sudo().get_param('calendar.block_synchro_attendee', default=True) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 023ade1b359..cbcab281e5a 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -426,13 +426,7 @@ 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] + self._create_set_followers(vals) sheet = super(HrExpenseSheet, self).create(vals) self.check_consistency() return sheet @@ -484,6 +478,20 @@ class HrExpenseSheet(models.Model): users = self._get_users_to_subscribe() self.message_subscribe_users(user_ids=users.ids) + @api.model + def _create_set_followers(self, values): + # Add the followers at creation, so they can be notified + employee_id = values.get('employee_id') + if not employee_id: + return + + employee = self.env['hr.employee'].browse(employee_id) + users = self._get_users_to_subscribe(employee=employee) - self.env.user + values['message_follower_ids'] = [] + MailFollowers = self.env['mail.followers'] + for partner in users.mapped('partner_id'): + values['message_follower_ids'] += MailFollowers._add_follower_command(self._name, [], {partner.id: None}, {})[0] + @api.onchange('employee_id') def _onchange_employee_id(self): self.address_id = self.employee_id.address_home_id diff --git a/addons/l10n_ca/data/account_chart_template_after_data.xml b/addons/l10n_ca/data/account_chart_template_after_data.xml index a3c1e720b18..6216f65899b 100644 --- a/addons/l10n_ca/data/account_chart_template_after_data.xml +++ b/addons/l10n_ca/data/account_chart_template_after_data.xml @@ -8,7 +8,7 @@ <field name="property_account_payable_id" ref="chart2111_en"/> <field name="property_account_income_categ_id" ref="chart411_en"/> <field name="property_account_expense_categ_id" ref="chart5111_en"/> - <field name="property_stock_account_input_categ_id" ref="chart2141_en"/> + <field name="property_stock_account_input_categ_id" ref="chart2171_en"/> <field name="property_stock_account_output_categ_id" ref="chart1145_en"/> <field name="property_stock_valuation_account_id" ref="chart1141_en"/> <field name="income_currency_exchange_account_id" ref="chart42_en"/> diff --git a/addons/l10n_ca/data/l10n_ca_chart_data.xml b/addons/l10n_ca/data/l10n_ca_chart_data.xml index 0a463374290..8610079a78f 100644 --- a/addons/l10n_ca/data/l10n_ca_chart_data.xml +++ b/addons/l10n_ca/data/l10n_ca_chart_data.xml @@ -217,7 +217,7 @@ <record id="chart2171_en" model="account.account.template"> <field name="code">2171</field> - <field name="user_type_id" ref="account.data_account_type_current_liabilities"/> + <field name="user_type_id" ref="account.data_account_type_current_assets"/> <field name="name">Stock Received But Not Billed</field> <field name="chart_template_id" ref="ca_en_chart_template_en"/> </record> diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index 215ed49cb4e..5b5bcc0fc88 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -5,10 +5,12 @@ import base64 import csv +from datetime import datetime import StringIO from odoo import api, fields, models, _ from odoo.exceptions import Warning +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT class AccountFrFec(models.TransientModel): @@ -33,7 +35,7 @@ class AccountFrFec(models.TransientModel): SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, - 'Balance initiale PL' AS EcritureNum, + 'OUVERTURE/' || %s AS EcritureNum, %s AS EcritureDate, '120/129' AS CompteNum, 'Benefice (perte) reporte(e)' AS CompteLib, @@ -67,8 +69,10 @@ class AccountFrFec(models.TransientModel): ''' company = self.env.user.company_id formatted_date_from = self.date_from.replace('-', '') + date_from = datetime.strptime(self.date_from, DEFAULT_SERVER_DATE_FORMAT) + formatted_date_year = date_from.year self._cr.execute( - sql_query, (formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) + sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) listrow = [] row = self._cr.fetchone() listrow = list(row) @@ -130,7 +134,7 @@ class AccountFrFec(models.TransientModel): SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, - 'Balance initiale ' || MIN(aa.name) AS EcritureNum, + 'OUVERTURE/' || %s AS EcritureNum, %s AS EcritureDate, MIN(aa.code) AS CompteNum, replace(MIN(aa.name), '|', '/') AS CompteLib, @@ -170,8 +174,10 @@ class AccountFrFec(models.TransientModel): HAVING sum(aml.balance) != 0 ''' formatted_date_from = self.date_from.replace('-', '') + date_from = datetime.strptime(self.date_from, DEFAULT_SERVER_DATE_FORMAT) + formatted_date_year = date_from.year self._cr.execute( - sql_query, (formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) + sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) for row in self._cr.fetchall(): listrow = list(row) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 12e036bb2d5..21be5d5b4ba 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -161,7 +161,7 @@ class StockMove(models.Model): def create_lots(self): lots = self.env['stock.move.lots'] for move in self: - unlink_move_lots = move.move_lot_ids.filtered(lambda x : (x.quantity_done == 0) and not x.workorder_id) + unlink_move_lots = move.move_lot_ids.filtered(lambda x : (x.quantity_done == 0) and x.done_wo) unlink_move_lots.sudo().unlink() group_new_quant = {} old_move_lot = {} diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 6d3199f4a18..8fece8eb81f 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -858,14 +858,11 @@ class SaleOrderLine(models.Model): if self.order_id.pricelist_id.discount_policy == 'with_discount': return product.with_context(pricelist=self.order_id.pricelist_id.id).price final_price, rule_id = self.order_id.pricelist_id.get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) - pricelist_item = self.env['product.pricelist.item'].browse(rule_id) - if pricelist_item.base == 'pricelist': - base_price, rule_id = pricelist_item.base_pricelist_id.get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) - base_price = pricelist_item.base_pricelist_id.currency_id.compute(base_price, self.order_id.pricelist_id.currency_id) - else: - base_price = product[pricelist_item.base] if pricelist_item else product.lst_price - base_price = product.currency_id.compute(base_price, self.order_id.pricelist_id.currency_id) - # negative discounts (= surcharge) are included in the display price (= unit price) + context_partner = dict(self.env.context, partner_id=self.order_id.partner_id.id, date=self.order_id.date_order) + base_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id) + if currency_id != self.order_id.pricelist_id.currency_id.id: + base_price = self.env['res.currency'].browse(currency_id).with_context(context_partner).compute(base_price, self.order_id.pricelist_id.currency_id) + # negative discounts (= surcharge) are included in the display price return max(base_price, final_price) @api.multi @@ -1008,11 +1005,10 @@ class SaleOrderLine(models.Model): price, rule_id = self.order_id.pricelist_id.with_context(pricelist_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id) - new_list_price = self.env['account.tax']._fix_tax_included_price_company(new_list_price, self.product_id.taxes_id, self.tax_id, self.company_id) if new_list_price != 0: - if self.product_id.company_id and self.order_id.pricelist_id.currency_id != self.product_id.company_id.currency_id: - # new_list_price is in company's currency while price in pricelist currency + if self.order_id.pricelist_id.currency_id.id != currency_id: + # we need new_list_price in the same currency as price, which is in the SO's pricelist's currency new_list_price = self.env['res.currency'].browse(currency_id).with_context(context_partner).compute(new_list_price, self.order_id.pricelist_id.currency_id) discount = (new_list_price - price) / new_list_price * 100 if discount > 0: diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 5ab4aca782c..3c063ba60dc 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -218,7 +218,7 @@ odoo.define('website.website', function (require) { init_kanban(this); }); - $('.js_website_submit_form').on('submit', function () { + $('body').on('submit', '.js_website_submit_form', function() { var $buttons = $(this).find('button[type="submit"], a.a-submit'); _.each($buttons, function (btn) { $(btn).attr('data-loading-text', '<i class="fa fa-spinner fa-spin"></i> ' + $(btn).text()).button('loading'); diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js index cb531d0035a..2038494a3e5 100644 --- a/addons/website_event/static/src/js/website_event.js +++ b/addons/website_event/static/src/js/website_event.js @@ -13,6 +13,7 @@ var EventRegistrationForm = Widget.extend({ .off('click') .removeClass('a-submit') .click(function (ev) { + $(this).attr('disabled', true); self.on_click(ev); }); }); @@ -28,7 +29,8 @@ var EventRegistrationForm = Widget.extend({ }); return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) { var $modal = $(modal); - $modal.appendTo($form).modal(); + $modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19 + $modal.after($form).modal(); $modal.on('click', '.js_goto_event', function () { $modal.modal('hide'); }); diff --git a/addons/website_event/views/website_event_templates.xml b/addons/website_event/views/website_event_templates.xml index 2004ed074ea..80738d60c30 100644 --- a/addons/website_event/views/website_event_templates.xml +++ b/addons/website_event/views/website_event_templates.xml @@ -466,7 +466,7 @@ <template id="registration_attendee_details" name="Registration Attendee Details"> <div id="modal_attendees_registration" class="modal fade" tabindex="-1" role="dialog"> <div class="modal-dialog modal-lg"> - <form id="attendee_registration" t-attf-action="/event/#{slug(event)}/registration/confirm" method="post"> + <form id="attendee_registration" t-attf-action="/event/#{slug(event)}/registration/confirm" method="post" class="js_website_submit_form"> <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/> <div class="modal-content"> <div class="modal-header"> @@ -474,7 +474,7 @@ <h4 class="modal-title" id="myModalLabel"><strong>Attendees</strong></h4> </div> <div class="modal-body"> - <div class="container"> + <div class=""> <t t-set="counter_type" t-value="1"/> <t t-set="counter" t-value="0"/> <t t-foreach="tickets" t-as="ticket"> diff --git a/addons/website_event_questions/views/website_event_questions_templates.xml b/addons/website_event_questions/views/website_event_questions_templates.xml index 6b47483fd44..b27abbfd259 100644 --- a/addons/website_event_questions/views/website_event_questions_templates.xml +++ b/addons/website_event_questions/views/website_event_questions_templates.xml @@ -30,7 +30,7 @@ </xpath> <!-- Generic questions --> - <xpath expr="//div[@class='container']/t[last()]" position="after"> + <xpath expr="//div[hasclass('modal-body')]/div/t[last()]" position="after"> <t t-if="event.general_question_ids"> <h4 class="page-header mt8"><strong>Common Questions</strong></h4> <div> diff --git a/addons/website_mail_channel/controllers/main.py b/addons/website_mail_channel/controllers/main.py index 4a9e2c449a8..3aa9491bf54 100644 --- a/addons/website_mail_channel/controllers/main.py +++ b/addons/website_mail_channel/controllers/main.py @@ -228,15 +228,19 @@ class MailGroup(http.Controller): def confirm_unsubscribe(self, channel, partner_id, token, **kw): subscriber = request.env['mail.channel.partner'].search([('channel_id', '=', channel.id), ('partner_id', '=', partner_id)]) if not subscriber: + partner = request.env['res.partner'].browse(partner_id).sudo().exists() # FIXME: remove try/except in master try: - return request.render( - 'website_mail_channel.not_subscribed', { - 'partner_id': partner_id - }) + response = request.render( + 'website_mail_channel.not_subscribed', + {'partner_id': partner}) + # make sure the rendering (and thus error if template is + # missing) happens inside the try block + response.flatten() + return response except ValueError: return _("The address %s is already unsubscribed or was never subscribed to any mailing list") % ( - partner_id.email + partner.email ) subscriber_token = channel._generate_action_token(partner_id, action='unsubscribe') diff --git a/addons/website_mail_channel/tests/__init__.py b/addons/website_mail_channel/tests/__init__.py new file mode 100644 index 00000000000..2e3364d701d --- /dev/null +++ b/addons/website_mail_channel/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import test_unsubscribe diff --git a/addons/website_mail_channel/tests/test_unsubscribe.py b/addons/website_mail_channel/tests/test_unsubscribe.py new file mode 100644 index 00000000000..44128c30b86 --- /dev/null +++ b/addons/website_mail_channel/tests/test_unsubscribe.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +import requests + +from odoo.api import Environment +from odoo.tests import common +from odoo.tools.misc import mute_logger + + +class TestConfirmUnsubscribe(common.HttpCase): + def setUp(self): + super(TestConfirmUnsubscribe, self).setUp() + + cr = self.registry.cursor() + # apparently HttpCase does not properly update self.env? + self.env2 = env = Environment(cr, self.uid, {}) + self.partner = env['res.partner'].create({ + 'name': 'Bob', + 'email': 'bob@bob.bob' + }) + self.mailing_list = env['mail.channel'].create({ + 'name': 'Test Mailing List', + 'public': 'public', + }) + self.token = self.mailing_list._generate_action_token(self.partner.id, action='unsubscribe') + + def test_not_subscribed(self): + """Test warning works""" + self.env2.cr.release() + + self._unsubscribe_check("The address %s is already unsubscribed" % self.partner.email) + + @mute_logger('odoo.addons.website.models.ir_ui_view') + def test_not_subscribed_no_template(self): + """ Test warning works on db without template (code update w/o module update) """ + self.env2.ref('website_mail_channel.not_subscribed').unlink() + self.assertEquals( + self.env2['ir.model.data'].search_count([ + ('module', '=', 'website_mail_channel'), + ('name', '=', 'not_subscribed'), + ]), 0, 'XID for template should have been deleted') + self.env2.cr.release() + + self._unsubscribe_check("The address %s is already unsubscribed or was never subscribed to any mailing list" % self.partner.email) + + def test_wrong_token(self): + self.mailing_list.sudo().write({ + 'channel_partner_ids': [(4, self.partner.id, False)] + }) + self.env2.cr.release() + self.token = 'XXX' + + self._unsubscribe_check("Invalid or expired confirmation link.") + + def test_successful_unsubscribe(self): + self.mailing_list.sudo().write({ + 'channel_partner_ids': [(4, self.partner.id, False)] + }) + self.env2.cr.release() + + self._unsubscribe_check("You have been correctly unsubscribed") + + def _unsubscribe_check(self, text): + url = "/groups/unsubscribe/{}/{}/{}".format( + self.mailing_list.id, self.partner.id, + self.token + ) + # FIXME: may need to be altered during 11.0 forward port + r = self.url_open(url) + body = r.read().decode('utf-8') + # normalize space to make matching simpler + self.assertIn(text, u' '.join(body.split())) |
