Merge branch 'master' of gitlab.com:flectra-hq/flectra

This commit is contained in:
Parthiv Patel committed 2021-05-31 08:25:46 +00:00
commit 0dd70e4eb9
107 files changed
+78879 -41490

No files matched your search

@@ -6,4 +6,4 @@ furniture_certification_answer_1_p1_q2_3,furniture_certification_answer_1,furnit
furniture_certification_answer_1_p1_q3,furniture_certification_answer_1,furniture_certification_page_1_question_3,False,text_box,,,,"I really liked the videos, you are awesome!",,
furniture_certification_answer_2_p1_q1,furniture_certification_answer_2,furniture_certification_page_1_question_1,False,suggestion,,,,,furniture_certification_page_1_question_1_choice_3,
furniture_certification_answer_2_p1_q2,furniture_certification_answer_2,furniture_certification_page_1_question_2,False,suggestion,,,,,furniture_certification_page_1_question_2_choice_4,
furniture_certification_answer_2_p1_q3,furniture_certification_answer_2,furniture_certification_page_1_question_3,true,,,,,,,
furniture_certification_answer_2_p1_q3,furniture_certification_answer_2,furniture_certification_page_1_question_3,true,,,,,,,
1 id user_input_id:id question_id:id skipped answer_type value_char_box value_numerical_box value_date value_text_box suggested_answer_id:id matrix_row_id:id
6 furniture_certification_answer_1_p1_q3 furniture_certification_answer_1 furniture_certification_page_1_question_3 False text_box I really liked the videos, you are awesome!
7 furniture_certification_answer_2_p1_q1 furniture_certification_answer_2 furniture_certification_page_1_question_1 False suggestion furniture_certification_page_1_question_1_choice_3
8 furniture_certification_answer_2_p1_q2 furniture_certification_answer_2 furniture_certification_page_1_question_2 False suggestion furniture_certification_page_1_question_2_choice_4
9 furniture_certification_answer_2_p1_q3 furniture_certification_answer_2 furniture_certification_page_1_question_3 true
+1 -1
View File
@@ -24,7 +24,7 @@
<field name="zip">94134</field>
<field name='country_id' ref='base.us'/>
<field name='state_id' ref='state_us_5'/>
<field name="phone">+1 (650) 691-3277 </field>
<field name="phone">+1 (650) 555-0111 </field>
<field name="email">info@yourcompany.example.com</field>
<field name="website">www.example.com</field>
</record>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,10 +1,10 @@
# Translation of Odoo Server.
# Translation of Flectra Server.
# This file contains the translation of the following modules:
# * base
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Project-Id-Version: Flectra Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-08 07:02+0000\n"
"PO-Revision-Date: 2017-10-24 09:14+0000\n"
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+12318 -5882
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+11530 -5166
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+10321 -5182
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+37 -2
View File
@@ -19,7 +19,8 @@ import idna
from flectra import api, fields, models, tools, _
from flectra.exceptions import UserError
from flectra.tools import ustr, pycompat, formataddr
from flectra.tools import ustr, pycompat, formataddr, encapsulate_email, email_domain_extract
_logger = logging.getLogger(__name__)
_test_logger = logging.getLogger('flectra.tests')
@@ -284,7 +285,7 @@ class IrMailServer(models.Model):
"""
email_from = email_from or self._get_default_from_address()
assert email_from, "You must either provide a sender address explicitly or configure "\
"using the combintion of `mail.catchall.domain` and `mail.default.from` "\
"using the combination of `mail.catchall.domain` and `mail.default.from` "\
"ICPs, in the server configuration file or with the "\
"--email-from startup parameter."
@@ -305,7 +306,12 @@ class IrMailServer(models.Model):
if references:
msg['references'] = references
msg['Subject'] = subject
email_from, return_path = self._get_email_from(email_from)
msg['From'] = email_from
if return_path:
headers.setdefault('Return-Path', return_path)
del msg['Reply-To']
msg['Reply-To'] = reply_to or email_from
msg['To'] = email_to
@@ -333,6 +339,35 @@ class IrMailServer(models.Model):
msg.add_attachment(fcontent, maintype, subtype, filename=fname)
return msg
def _get_email_from(self, email_from):
"""Logic which determines which email to use when sending the email.
- If the system parameter `mail.force.smtp.from` is set we encapsulate all
outgoing email from
- If the previous system parameter is not set and if both `mail.dynamic.smtp.from`
and `mail.catchall.domain` are set, we encapsulate the FROM only if the domain
of the email is not the same as the domain of the catchall parameter
- Otherwise we do not encapsulate the email and given email_from is used as is
:param email_from: The initial FROM headers
:return: The FROM to used in the headers and optionally the Return-Path
"""
force_smtp_from = self.env['ir.config_parameter'].sudo().get_param('mail.force.smtp.from')
dynamic_smtp_from = self.env['ir.config_parameter'].sudo().get_param('mail.dynamic.smtp.from')
catchall_domain = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.domain')
if force_smtp_from:
rfc2822_force_smtp_from = extract_rfc2822_addresses(force_smtp_from)
rfc2822_force_smtp_from = rfc2822_force_smtp_from[0] if rfc2822_force_smtp_from else None
return encapsulate_email(email_from, force_smtp_from), rfc2822_force_smtp_from
elif dynamic_smtp_from and catchall_domain and email_domain_extract(email_from) != catchall_domain:
rfc2822_dynamic_smtp_from = extract_rfc2822_addresses(dynamic_smtp_from)
rfc2822_dynamic_smtp_from = rfc2822_dynamic_smtp_from[0] if rfc2822_dynamic_smtp_from else None
return encapsulate_email(email_from, dynamic_smtp_from), rfc2822_dynamic_smtp_from
return email_from, None
@api.model
def _get_default_bounce_address(self):
'''Compute the default bounce address.
+14 -2
View File
@@ -430,8 +430,18 @@ class IrModel(models.Model):
if tools.table_kind(cr, Model._table) not in ('r', None):
# not a regular table, so disable schema upgrades
Model._auto = False
cr.execute('SELECT * FROM %s LIMIT 0' % Model._table)
columns = {desc[0] for desc in cr.description}
cr.execute(
'''
SELECT a.attname
FROM pg_attribute a
JOIN pg_class t
ON a.attrelid = t.oid
AND t.relname = %s
WHERE a.attnum > 0 -- skip system columns
''',
[Model._table]
)
columns = {colinfo[0] for colinfo in cr.fetchall()}
Model._log_access = set(models.LOG_ACCESS_COLUMNS) <= columns
@@ -1023,6 +1033,8 @@ class IrModelFields(models.Model):
model_id = self.env['ir.model']._get_id(model_name)
for field in self.env[model_name]._fields.values():
rows.append(self._reflect_field_params(field, model_id))
if not rows:
return
cols = list(unique(['model', 'name'] + list(rows[0])))
expected = [tuple(row[col] for col in cols) for row in rows]
+3 -2
View File
@@ -15,6 +15,7 @@ import threading
import zipfile
import requests
import werkzeug.urls
from docutils import nodes
from docutils.core import publish_string
@@ -784,7 +785,7 @@ class Module(models.Model):
_logger.warning(msg)
raise UserError(msg)
apps_server = urls.url_parse(self.get_apps_server())
apps_server = werkzeug.urls.url_parse(self.get_apps_server())
OPENERP = flectra.release.product_name.lower()
tmp = tempfile.mkdtemp()
@@ -795,7 +796,7 @@ class Module(models.Model):
if not url:
continue # nothing to download, local version is already the last one
up = urls.url_parse(url)
up = werkzeug.urls.url_parse(url)
if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc:
raise AccessDenied()
+2 -1
View File
@@ -224,12 +224,13 @@ class IrSequence(models.Model):
return res
self.ensure_one()
d = _interpolation_dict()
try:
interpolated_prefix = _interpolate(self.prefix, d)
interpolated_suffix = _interpolate(self.suffix, d)
except ValueError:
raise UserError(_('Invalid prefix or suffix for sequence \'%s\'') % (self.get('name')))
raise UserError(_('Invalid prefix or suffix for sequence \'%s\'') % self.name)
return interpolated_prefix, interpolated_suffix
def get_next_char(self, number_next):
+1 -1
View File
@@ -517,7 +517,7 @@ class Partner(models.Model):
self.invalidate_cache(['user_ids'], self._ids)
for partner in self:
if partner.active and partner.user_ids:
raise ValidationError(_('You cannot archive a contact linked to an internal user.'))
raise ValidationError(_('You cannot archive a contact linked to a portal or internal user.'))
# res.partner must only allow to set the company_id of a partner if it
# is the same as the company of all users that inherit from this partner
# (this is to allow the code from res_users to write to the partner!) or
+1 -1
View File
@@ -948,7 +948,7 @@ class Users(models.Model):
"and *might* be a proxy. If your Flectra is behind a proxy, "
"it may be mis-configured. Check that you are running "
"Flectra in Proxy Mode and that the proxy is properly configured, see "
"https://www.flectrahq.com/documentation/14.0/setup/deploy.html#https for details.",
"https://doc.flectrahq.com/2.0/administration/deployment/deploy.html#https for details.",
source
)
raise AccessDenied(_("Too many login failures, please wait a bit before trying again."))
+1 -1
View File
@@ -19,7 +19,7 @@ stream
BT
/F1 100 Tf
10 400 Td
(!Flectra Test!) Tj
(!Odoo Test!) Tj
ET
endstream
endobj
@@ -114,6 +114,17 @@ class TestExpression(SavepointCaseWithUserDemo):
test('not ilike', 'B', ['0', 'a'])
test('not like', 'AB', ['0', 'a', 'b', 'a b'])
def test_09_hierarchy_filtered_domain(self):
Partner = self.env['res.partner']
p = Partner.create({'name': 'dummy'})
# hierarchy without parent
self.assertFalse(p.parent_id)
p2 = self._search(Partner, [('parent_id', 'child_of', p.id)], [('id', '=', p.id)])
self.assertEqual(p2, p)
p3 = self._search(Partner, [('parent_id', 'parent_of', p.id)], [('id', '=', p.id)])
self.assertEqual(p3, p)
def test_10_hierarchy_in_m2m(self):
Partner = self.env['res.partner']
Category = self.env['res.partner.category']
+2 -2
View File
@@ -510,8 +510,8 @@ class TestCustomFields(common.TransactionCase):
})
query_count = self.cr.sql_log_count - query_count
# create the related field, and assert it only takes 3 extra queries
with self.assertQueryCount(query_count + 3):
# create the related field, and assert it only takes 1 extra queries
with self.assertQueryCount(query_count + 1):
self.env['ir.model.fields'].create({
'model_id': self.env['ir.model']._get_id('res.partner'),
'name': 'x_oh_boy',
+70
View File
@@ -447,6 +447,76 @@ class EmailConfigCase(SavepointCase):
)
self.assertEqual(message["From"], "settings@example.com")
def test_email_from_rewrite(self):
get_email_from = self.env['ir.mail_server']._get_email_from
set_param = self.env['ir.config_parameter'].set_param
# Standard case, no setting
set_param('mail.force.smtp.from', False)
set_param('mail.dynamic.smtp.from', False)
set_param('mail.catchall.domain', 'flectra.example.com')
email_from, return_path = get_email_from('admin@test.example.com')
self.assertEqual(email_from, 'admin@test.example.com')
self.assertFalse(return_path)
email_from, return_path = get_email_from('"Admin" <admin@test.example.com>')
self.assertEqual(email_from, '"Admin" <admin@test.example.com>')
self.assertFalse(return_path)
# We always force the email FROM
set_param('mail.force.smtp.from', 'email_force@domain.com')
set_param('mail.dynamic.smtp.from', False)
set_param('mail.catchall.domain', 'flectra.example.com')
email_from, return_path = get_email_from('admin@test.example.com')
self.assertEqual(email_from, '"admin@test.example.com" <email_force@domain.com>')
self.assertEqual(return_path, 'email_force@domain.com')
email_from, return_path = get_email_from('"Admin" <admin@test.example.com>')
self.assertEqual(email_from, '"Admin (admin@test.example.com)" <email_force@domain.com>')
self.assertEqual(return_path, 'email_force@domain.com')
# We always force the email FROM (notification email contains a name part)
set_param('mail.force.smtp.from', '"Your notification bot" <email_force@domain.com>')
set_param('mail.dynamic.smtp.from', False)
set_param('mail.catchall.domain', 'flectra.example.com')
email_from, return_path = get_email_from('"Admin" <admin@test.example.com>')
self.assertEqual(email_from, '"Admin (admin@test.example.com)" <email_force@domain.com>',
msg='Should drop the name part of the forced email')
self.assertEqual(return_path, 'email_force@domain.com')
# We dynamically force the email FROM
set_param('mail.force.smtp.from', False)
set_param('mail.dynamic.smtp.from', 'notification@flectra.example.com')
set_param('mail.catchall.domain', 'flectra.example.com')
email_from, return_path = get_email_from('"Admin" <admin@test.example.com>')
self.assertEqual(email_from, '"Admin (admin@test.example.com)" <notification@flectra.example.com>',
msg='Domain is not the same as the catchall domain, we should force the email FROM')
self.assertEqual(return_path, 'notification@flectra.example.com')
email_from, return_path = get_email_from('"Admin" <admin@flectra.example.com>')
self.assertEqual(email_from, '"Admin" <admin@flectra.example.com>',
msg='Domain is the same as the catchall domain, we should not force the email FROM')
self.assertFalse(return_path)
# We dynamically force the email FROM (notification email contains a name part)
set_param('mail.force.smtp.from', False)
set_param('mail.dynamic.smtp.from', '"Your notification bot" <notification@flectra.example.com>')
set_param('mail.catchall.domain', 'flectra.example.com')
email_from, return_path = get_email_from('"Admin" <admin@test.example.com>')
self.assertEqual(email_from, '"Admin (admin@test.example.com)" <notification@flectra.example.com>',
msg='Domain is not the same as the catchall domain, we should force the email FROM')
self.assertEqual(return_path, 'notification@flectra.example.com')
email_from, return_path = get_email_from('"Admin" <admin@flectra.example.com>')
self.assertEqual(email_from, '"Admin" <admin@flectra.example.com>',
msg='Domain is the same as the catchall domain, we should not force the email FROM')
self.assertFalse(return_path)
class TestEmailMessage(TransactionCase):
def test_as_string(self):
+24
View File
@@ -557,6 +557,30 @@ class TestQWebNS(TransactionCase):
rendered = view2.with_context(lang=current_lang)._render().strip()
self.assertEqual(rendered, b'9/000/000*00')
def test_render_barcode(self):
partner = self.env['res.partner'].create({
'name': 'bacode_test',
'barcode': 'test'
})
view = self.env['ir.ui.view'].create({
'name': "a_barcode_view",
'type': 'qweb',
})
view.arch = u"""<div t-field="partner.barcode" t-options="{'widget': 'barcode', 'width': 100, 'height': 30}"/>"""
rendered = view._render(values={'partner': partner}).strip().decode()
self.assertRegex(rendered, r'<div><img alt="Barcode test" src="data:image/png;base64,\S+"></div>')
partner.barcode = '4012345678901'
view.arch = u"""<div t-field="partner.barcode" t-options="{'widget': 'barcode', 'symbology': 'EAN13', 'width': 100, 'height': 30, 'img_style': 'width:100%;', 'img_alt': 'Barcode'}"/>"""
ean_rendered = view._render(values={'partner': partner}).strip().decode()
self.assertRegex(ean_rendered, r'<div><img style="width:100%;" alt="Barcode" src="data:image/png;base64,\S+"></div>')
view.arch = u"""<div t-field="partner.barcode" t-options="{'widget': 'barcode', 'symbology': 'auto', 'width': 100, 'height': 30, 'img_style': 'width:100%;', 'img_alt': 'Barcode'}"/>"""
auto_rendered = view._render(values={'partner': partner}).strip().decode()
self.assertRegex(auto_rendered, r'<div><img style="width:100%;" alt="Barcode" src="data:image/png;base64,\S+"></div>')
from copy import deepcopy
class FileSystemLoader(object):
@@ -179,7 +179,7 @@
</a>
<div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<a type="edit" class="dropdown-item">Module Info</a>
<a t-if="record.website.raw_value" role="menuitem" class="dropdown-item" t-att-href="record.website.raw_value" target="_blank">Learn More</a>
<a t-if="record.website.raw_value" role="menuitem" class="dropdown-item o-hidden-ios" t-att-href="record.website.raw_value" target="_blank">Learn More</a>
<a t-if="installed" name="button_immediate_upgrade" type="object" role="menuitem" class="dropdown-item">Upgrade</a>
<a t-if="installed" name="button_uninstall_wizard" type="object" role="menuitem" class="dropdown-item">Uninstall</a>
</div>
@@ -194,7 +194,7 @@
<code groups="base.group_no_one"><field name="name"/></code>
</p>
<div class="oe_module_action">
<a t-att-href="record.website.raw_value" target="_blank" attrs="{'invisible':[('website', '=', '')]}" class="btn btn-sm btn-secondary float-right" role="button">Learn More</a>
<a t-att-href="record.website.raw_value" target="_blank" attrs="{'invisible':[('website', '=', '')]}" class="btn btn-sm btn-secondary float-right o-hidden-ios" role="button">Learn More</a>
<a type="edit" class="btn btn-secondary btn-sm float-right" role="button" attrs="{'invisible': [('website', '&lt;&gt;', '')]}">Module Info</a>
<button type="object" class="btn btn-primary btn-sm" name="button_immediate_install" states="uninstalled" t-if="! record.to_buy.raw_value">Install</button>
<a href="https://flectrahq.com/pricing?utm_source=db&amp;utm_medium=module#hosting=on_premise" class="btn btn-info btn-sm" states="uninstalled,uninstallable" t-if="record.to_buy.raw_value" role="button">Upgrade</a>
@@ -468,7 +468,7 @@
</div>
</group>
<group string="Developer API Keys" help="test">
<a href="https://www.flectrahq.com/documentation/master/webservices/flectra.html#api-keys" target="_blank">
<a href="https://doc.flectrahq.com/2.0/developer/webservices/flectra.html#api-keys" target="_blank">
<i class="fa fa-fw o_button_icon fa-arrow-right"></i>
<span>Documentation</span>
</a>
@@ -12,7 +12,7 @@ class FlectraBaseChecker(checkers.BaseChecker):
'E8502': (
'Bad usage of _, _lt function.',
'gettext-variable',
'See https://flectrahq.com/documentation/14.0/reference/translations.html#variables'
'See https://doc.flectrahq.com/2.0/reference/translations.html#variables'
)
}
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
# pylint: disable=unbalanced-tuple-unpacking
import logging
import re
@@ -13,13 +14,16 @@ _logger = logging.getLogger(__name__)
TSTRING_RE = re.compile(r'_l?t\(\s*`.*?\s*`\s*\)', re.DOTALL)
EXPRESSION_RE = re.compile(r'\$\{.+?\}')
UNDERSCORE_RE = re.compile(r'\b_\(\s*[\'"]')
class TestJsTranslations(lint_case.LintCase):
def check_text(self, text):
""" Search for translated template strings that contains an expression
:param text: The js text to search
:return: A list of tuple with line number and invalid template string or an empty list
""" Search for translation errors in the text
:param text: The js text to search
:return: A list of tuple with line number and invalid template string,
or None for underscore errors
"""
error_list = list()
for m in TSTRING_RE.finditer(text):
@@ -27,6 +31,11 @@ class TestJsTranslations(lint_case.LintCase):
if EXPRESSION_RE.search(template_string):
line_nb = text[:m.start()].count('\n') + 1
error_list.append((line_nb, template_string))
for m in UNDERSCORE_RE.finditer(text):
lineno = text[:m.start()].count('\n') + 1
error_list.append((lineno, None))
return error_list
def test_regular_expression(self):
@@ -39,11 +48,10 @@ class TestJsTranslations(lint_case.LintCase):
"""
error_list = self.check_text(bad_js)
self.assertEqual(len(error_list), 1)
for line, template_string in error_list:
template_string = error_list[0][1]
self.assertIn('invalid template-string', template_string)
self.assertNotIn('but valid template-string', template_string)
self.assertEqual(line, 4)
[(line, template_string)] = error_list
self.assertEqual(line, 4)
self.assertIn('invalid template-string', template_string)
self.assertNotIn('but valid template-string', template_string)
def test_regular_expression_long(self):
bad_js = """
@@ -54,25 +62,49 @@ class TestJsTranslations(lint_case.LintCase):
error_list = self.check_text(bad_js)
self.assertEqual(len(error_list), 1)
for line, template_string in error_list:
template_string = error_list[0][1]
self.assertIn('foo ${this + is(a, very) - long == expression}', template_string)
self.assertEqual(line, 2)
[(line, template_string)] = error_list
self.assertEqual(line, 2)
self.assertIn('foo ${this + is(a, very) - long == expression}', template_string)
def test_matches_underscore(self):
bad_js = """
const thing1 = _('literal0');
const thing0 = _([]);
const thing2 = _("literal1");
"""
self.assertEqual(
self.check_text(bad_js),
[(2, None), (4, None)]
)
def test_js_translations(self):
""" Test that there are no translation of JS template strings """
""" Test that there are no translation of JS template strings or underscore
calls misused as translation markers
"""
counter = 0
failures = 0
for js_file in self.iter_module_files('*.js'):
# lodash has string methods and occurrences of `_('text')` in its comments
if js_file.endswith('/lodash.js'):
continue
counter += 1
with tools.file_open(js_file, 'r') as f:
js_txt = f.read()
error_list = self.check_text(js_txt)
for line_number, template_string in error_list:
failures += 1
mod, relative_path, _ = get_resource_from_path(js_file)
_logger.error("Translation of a template string found in `%s/%s` at line %s: %s", mod, relative_path, line_number, template_string)
error_list = self.check_text(js_txt)
for line_number, template_string in error_list:
failures += 1
mod, relative_path, _ = get_resource_from_path(js_file)
if template_string:
prefix = "Translation of a template string"
suffix = template_string
else:
prefix = "underscore.js used as translation function"
suffix = "_t and _lt are the JS translation functions"
_logger.error("%s found in `%s/%s` at line %s: %s", prefix, mod, relative_path, line_number, suffix)
_logger.info('%s files tested', counter)
if failures > 0:
+2 -2
View File
@@ -1,10 +1,10 @@
# Translation of Odoo Server.
# Translation of Flectra Server.
# This file contains the translation of the following modules:
# * test_new_api
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0alpha1\n"
"Project-Id-Version: Flectra Server 13.0alpha1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-07-08 06:43+0000\n"
"PO-Revision-Date: 2019-07-08 06:43+0000\n"
@@ -1,10 +1,10 @@
# Translation of Odoo Server.
# Translation of Flectra Server.
# This file contains the translation of the following modules:
# * test_new_api
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0alpha1\n"
"Project-Id-Version: Flectra Server 13.0alpha1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-07-08 06:43+0000\n"
"PO-Revision-Date: 2019-07-08 06:43+0000\n"
@@ -692,6 +692,40 @@ class ComputeUnassigned(models.Model):
record.bares = record.foo
class ComputeOne2many(models.Model):
_name = 'test_new_api.one2many'
_description = "A computed editable one2many field with a domain"
name = fields.Char()
line_ids = fields.One2many(
'test_new_api.one2many.line', 'container_id',
compute='_compute_line_ids', store=True, readonly=False,
domain=[('count', '>', 0)],
)
@api.depends('name')
def _compute_line_ids(self):
# increment counter of line with the same name, or create a new line
for record in self:
if not record.name:
continue
for line in record.line_ids:
if line.name == record.name:
line.count += 1
break
else:
record.line_ids = [(0, 0, {'name': record.name})]
class ComputeOne2manyLine(models.Model):
_name = 'test_new_api.one2many.line'
_description = "Line of a computed one2many"
name = fields.Char()
count = fields.Integer(default=1)
container_id = fields.Many2one('test_new_api.one2many', required=True)
class ModelBinary(models.Model):
_name = 'test_new_api.model_binary'
_description = 'Test Image field'
@@ -29,6 +29,8 @@ access_test_new_api_compute_onchange,access_test_new_api_compute_onchange,model_
access_test_new_api_compute_onchange_line,access_test_new_api_compute_onchange_line,model_test_new_api_compute_onchange_line,,1,1,1,1
access_test_new_api_compute_dynamic_depends,access_test_new_api_compute_dynamic_depends,model_test_new_api_compute_dynamic_depends,,1,1,1,1
access_test_new_api_compute_unassigned,access_test_new_api_compute_unassigned,model_test_new_api_compute_unassigned,,1,1,1,1
access_test_new_api_one2many,access_test_new_api_one2many,model_test_new_api_one2many,,1,1,1,1
access_test_new_api_one2many_line,access_test_new_api_one2many_line,model_test_new_api_one2many_line,,1,1,1,1
access_test_new_api_binary_svg,access_test_new_api_binary_svg,model_test_new_api_binary_svg,,1,1,1,1
access_test_new_api_monetary_base,access_test_new_api_monetary_base,model_test_new_api_monetary_base,,1,1,1,1
access_test_new_api_monetary_related,access_test_new_api_monetary_related,model_test_new_api_monetary_related,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
29 access_test_new_api_compute_onchange_line access_test_new_api_compute_onchange_line model_test_new_api_compute_onchange_line 1 1 1 1
30 access_test_new_api_compute_dynamic_depends access_test_new_api_compute_dynamic_depends model_test_new_api_compute_dynamic_depends 1 1 1 1
31 access_test_new_api_compute_unassigned access_test_new_api_compute_unassigned model_test_new_api_compute_unassigned 1 1 1 1
32 access_test_new_api_one2many access_test_new_api_one2many model_test_new_api_one2many 1 1 1 1
33 access_test_new_api_one2many_line access_test_new_api_one2many_line model_test_new_api_one2many_line 1 1 1 1
34 access_test_new_api_binary_svg access_test_new_api_binary_svg model_test_new_api_binary_svg 1 1 1 1
35 access_test_new_api_monetary_base access_test_new_api_monetary_base model_test_new_api_monetary_base 1 1 1 1
36 access_test_new_api_monetary_related access_test_new_api_monetary_related model_test_new_api_monetary_related 1 1 1 1
@@ -823,3 +823,47 @@ class TestComputeOnchange(common.TransactionCase):
]
result = record.onchange({'line_ids': line_ids}, 'line_ids', spec)
self.assertEqual(result, expected)
def test_computed_editable_one2many_domain(self):
""" Test a computed, editable one2many field with a domain. """
record = self.env['test_new_api.one2many'].create({'name': 'foo'})
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
])
# trigger recomputation by changing name
record.name = 'bar'
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
{'name': 'bar', 'count': 1},
])
# manually adding a line should not trigger recomputation
record.line_ids.create({'name': 'baz', 'container_id': record.id})
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
{'name': 'bar', 'count': 1},
{'name': 'baz', 'count': 1},
])
# changing the field in the domain should not trigger recomputation...
record.line_ids[-1].count = 2
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
{'name': 'bar', 'count': 1},
{'name': 'baz', 'count': 2},
])
# ...and may show cache inconsistencies
record.line_ids[-1].count = 0
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
{'name': 'bar', 'count': 1},
{'name': 'baz', 'count': 0},
])
record.flush()
record.invalidate_cache()
self.assertRecordValues(record.line_ids, [
{'name': 'foo', 'count': 1},
{'name': 'bar', 'count': 1},
])
@@ -1,8 +1,8 @@
# This is a test PO file, not a complete one. It is manually maintained
# to test the import translation behavior of Odoo.
# to test the import translation behavior of Flectra.
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.4\n"
"Project-Id-Version: Flectra Server saas~11.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-18 13:20+0000\n"
"PO-Revision-Date: 2018-09-18 13:20+0000\n"
+1 -1
View File
@@ -3021,7 +3021,7 @@ class _RelationalMulti(_Relational):
def _setup_regular_full(self, model):
super(_RelationalMulti, self)._setup_regular_full(model)
if isinstance(self.domain, list):
if not self.compute and isinstance(self.domain, list):
self.depends = tuple(unique(itertools.chain(self.depends, (
self.name + '.' + arg[0]
for arg in self.domain
+1 -1
View File
@@ -781,7 +781,7 @@ class HttpRequest(WebRequest):
Flectra URLs are CSRF-protected by default (when accessed with unsafe
HTTP methods). See
https://doc.flectrahq.com/2.0/reference/http.html#csrf for
https://doc.flectrahq.com/2.0/developer/reference/http.html#csrf for
more details.
* if this endpoint is accessed through Flectra via py-QWeb form, embed a CSRF
+4 -4
View File
@@ -911,7 +911,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if lines2:
# merge first line with record's main line
for j, val in enumerate(lines2[0]):
if val or isinstance(val, bool):
if val or isinstance(val, (int, float)):
current[j] = val
# append the other lines at the end
lines += lines2[1:]
@@ -5296,6 +5296,9 @@ Fields:
result.append(self.browse())
else:
(key, comparator, value) = d
if comparator in ('child_of', 'parent_of'):
result.append(self.search([('id', 'in', self.ids), d]))
continue
if key.endswith('.id'):
key = key[:-3]
if key == 'id':
@@ -5312,9 +5315,6 @@ Fields:
records_ids = OrderedSet()
for rec in self:
data = rec.mapped(key)
if comparator in ('child_of', 'parent_of'):
value = data.search([(data._parent_name, comparator, value)]).ids
comparator = 'in'
if isinstance(data, BaseModel):
v = value
if (isinstance(value, list) or isinstance(value, tuple)) and len(value):
+27
View File
@@ -88,6 +88,32 @@ class FlectraHook(object):
return sys.modules[name]
class UpgradeHook(object):
"""Makes the legacy `migrations` package being `flectra.upgrade`"""
def find_module(self, name, path=None):
if re.match(r"^flectra.addons.base.maintenance.migrations\b", name):
# We can't trigger a DeprecationWarning in this case.
# In order to be cross-versions, the multi-versions upgrade scripts (0.0.0 scripts),
# the tests, and the common files (utility functions) still needs to import from the
# legacy name.
return self
def load_module(self, name):
assert name not in sys.modules
canonical_upgrade = name.replace("flectra.addons.base.maintenance.migrations", "flectra.upgrade")
if canonical_upgrade in sys.modules:
mod = sys.modules[canonical_upgrade]
else:
mod = importlib.import_module(canonical_upgrade)
sys.modules[name] = mod
return sys.modules[name]
def initialize_sys_path():
"""
Setup the addons path ``flectra.addons.__path__`` with various defaults
@@ -126,6 +152,7 @@ def initialize_sys_path():
# hook deprecated module alias from openerp to flectra and "crm"-like to flectra.addons
if not getattr(initialize_sys_path, 'called', False): # only initialize once
sys.meta_path.insert(0, UpgradeHook())
sys.meta_path.insert(0, FlectraHook())
sys.meta_path.insert(0, AddonsHook())
initialize_sys_path.called = True
+1
View File
@@ -137,6 +137,7 @@ def init_logger():
'reportlab.lib.rl_safe_eval',# reportlab importing ABC from collections
'ofxparse',# ofxparse importing ABC from collections
'astroid', # deprecated imp module (fixed in 2.5.1)
'requests_toolbelt', # importing ABC from collections (fixed in 0.9)
]:
warnings.filterwarnings('ignore', category=DeprecationWarning, module=module)
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff. Show more