diff --git a/addons/account_tax_cash_basis/models/account_partial_reconcile.py b/addons/account_tax_cash_basis/models/account_partial_reconcile.py index b2999f421bd..703eba5801c 100644 --- a/addons/account_tax_cash_basis/models/account_partial_reconcile.py +++ b/addons/account_tax_cash_basis/models/account_partial_reconcile.py @@ -21,8 +21,9 @@ class AccountPartialReconcileCashBasis(models.Model): #TOCHECK: normal and cash basis taxes shoudn't be mixed together (on the same invoice line for example) as it will #create reporting issues. Not sure of the behavior to implement in that case, though. # amount to write is the current cash_basis amount minus the one before the reconciliation + currency_id = line.currency_id or line.company_id.currency_id matched_percentage = value_before_reconciliation[move.id] - amount = line.currency_id.round((line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage) + amount = currency_id.round((line.credit_cash_basis - line.debit_cash_basis) - (line.credit - line.debit) * matched_percentage) if not line.tax_exigible: if line.tax_line_id and line.tax_line_id.use_cash_basis: # group by line account @@ -42,16 +43,16 @@ class AccountPartialReconcileCashBasis(models.Model): for tax in line.tax_ids: line_to_create.append((0, 0, { 'name': '/', - 'debit': line.currency_id.round(line.debit_cash_basis - line.debit * matched_percentage), - 'credit': line.currency_id.round(line.credit_cash_basis - line.credit * matched_percentage), + 'debit': currency_id.round(line.debit_cash_basis - line.debit * matched_percentage), + 'credit': currency_id.round(line.credit_cash_basis - line.credit * matched_percentage), 'account_id': line.account_id.id, 'tax_ids': [(6, 0, [tax.id])], 'tax_exigible': True, })) line_to_create.append((0, 0, { 'name': '/', - 'credit': line.currency_id.round(line.debit_cash_basis - line.debit * matched_percentage), - 'debit': line.currency_id.round(line.credit_cash_basis - line.credit * matched_percentage), + 'credit': currency_id.round(line.debit_cash_basis - line.debit * matched_percentage), + 'debit': currency_id.round(line.credit_cash_basis - line.credit * matched_percentage), 'account_id': line.account_id.id, 'tax_exigible': True, })) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 716175a5fd8..2718e47dd2a 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -63,8 +63,7 @@ class AuthSignupHome(Home): qcontext['error'] = _("Could not reset your password") _logger.exception('error when resetting password') except Exception, e: - qcontext['error'] = e.message - + qcontext['error'] = e.message or e.name return request.render('auth_signup.reset_password', qcontext) diff --git a/addons/barcodes/static/src/js/barcode_events.js b/addons/barcodes/static/src/js/barcode_events.js index fca30916106..3c5cb289232 100644 --- a/addons/barcodes/static/src/js/barcode_events.js +++ b/addons/barcodes/static/src/js/barcode_events.js @@ -4,6 +4,16 @@ odoo.define('barcodes.BarcodeEvents', function(require) { var core = require('web.core'); var mixins = core.mixins; + +// For IE >= 9, use this, new CustomEvent(), instead of new Event() +function CustomEvent ( event, params ) { + params = params || { bubbles: false, cancelable: false, detail: undefined }; + var evt = document.createEvent( 'CustomEvent' ); + evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); + return evt; + } +CustomEvent.prototype = window.Event.prototype; + var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, { timeout: null, key_pressed: {}, @@ -63,10 +73,16 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, { // bug for the longest time that causes keyCode and // charCode to not be set for events created this way: // https://bugs.webkit.org/show_bug.cgi?id=16735 - new_event = new Event("keypress", { + var params = { 'bubbles': old_event.bubbles, 'cancelable': old_event.cancelable, - }); + }; + try { + new_event = new Event("keypress", params); + } catch(error) { + // For IE >= 9, use new CustomEvent(), instead of new Event() + new_event = new CustomEvent("keypress", params); + } new_event.viewArg = old_event.viewArg; new_event.ctrl = old_event.ctrl; diff --git a/addons/crm/data/crm_lead_demo.xml b/addons/crm/data/crm_lead_demo.xml index e5281090e28..a4b40a6d984 100644 --- a/addons/crm/data/crm_lead_demo.xml +++ b/addons/crm/data/crm_lead_demo.xml @@ -669,7 +669,7 @@ Andrew

]]> Birmingham B46 3AG - 81 Academy Avenue + Cannon Hill Park 2 diff --git a/addons/l10n_si/__manifest__.py b/addons/l10n_si/__manifest__.py index bb79727cdf1..1a06c04cdfd 100644 --- a/addons/l10n_si/__manifest__.py +++ b/addons/l10n_si/__manifest__.py @@ -5,6 +5,7 @@ { "name": "Slovenian - Accounting", + "versiom": "1.1", "author": "Mentis d.o.o.", "website": "http://www.mentis.si", 'category': 'Localization', diff --git a/addons/l10n_si/data/account.account.template.csv b/addons/l10n_si/data/account.account.template.csv index 92cbf7ee3b3..40b08e97e60 100644 --- a/addons/l10n_si/data/account.account.template.csv +++ b/addons/l10n_si/data/account.account.template.csv @@ -96,14 +96,14 @@ code,id,user_type_id/id,name,reconcile,chart_template_id/id 155000,gd_acc_155000,account.data_account_type_current_assets,"DRUGE KRATKOROČNE TERJATVE, POVEZANE S FINANČNIMI PRIHODKI",False,gd_chart 159000,gd_acc_159000,account.data_account_type_current_assets,"OSLABITEV VREDNOSTI KRATKOROČNIH TERJATEV, POVEZANIH S FINANČNIMI PRIHODKI",False,gd_chart 160001,gd_acc_160001,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA NEODBITNI DDV",False,gd_chart -160002,gd_acc_160002,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 8,5%",False,gd_chart -160003,gd_acc_160003,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 20%",False,gd_chart -160004,gd_acc_160004,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 8,5% V EU",False,gd_chart -160005,gd_acc_160005,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 20% V EU",False,gd_chart -160006,gd_acc_160006,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 8,5% IZVEN EU",False,gd_chart -160007,gd_acc_160007,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 20% IZVEN EU",False,gd_chart -160008,gd_acc_160008,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 8,5% UVOZ",False,gd_chart -160009,gd_acc_160009,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 20% UVOZ",False,gd_chart +160002,gd_acc_160002,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 9,5%",False,gd_chart +160003,gd_acc_160003,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 22%",False,gd_chart +160004,gd_acc_160004,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 9,5% V EU",False,gd_chart +160005,gd_acc_160005,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 22% V EU",False,gd_chart +160006,gd_acc_160006,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 9,5% IZVEN EU",False,gd_chart +160007,gd_acc_160007,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 22% IZVEN EU",False,gd_chart +160008,gd_acc_160008,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 9,5% UVOZ",False,gd_chart +160009,gd_acc_160009,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA ODBITNI DDV 22% UVOZ",False,gd_chart 161000,gd_acc_161000,account.data_account_type_current_assets,"KRATKOROČNE TERJATVE ZA DAVEK OD DOHODKOV PRAVNIH OSEB, VKLJUČNO Z DAVKOM, PLAČANIM V TUJINI",False,gd_chart 162000,gd_acc_162000,account.data_account_type_current_assets,"DRUGE KRATKOROČNE TERJATVE DO DRŽAVNIH IN DRUGIH INŠTITUCIJ",False,gd_chart 165000,gd_acc_165000,account.data_account_type_current_assets,"OSTALE KRATKOROČNE TERJATVE",False,gd_chart @@ -152,12 +152,12 @@ code,id,user_type_id/id,name,reconcile,chart_template_id/id 255000,gd_acc_255000,account.data_account_type_current_liabilities,"KRATKOROČNE OBVEZNOSTI ZA DRUGE PREJEMKE IZ DELOVNEGA RAZMERJA",False,gd_chart 256000,gd_acc_256000,account.data_account_type_current_liabilities,"KRATKOROČNE OBVEZNOSTI ZA PRISPEVKE IZ DRUGIH PREJEMKOV IZ DELOVNEGA RAZMERJA, KI SE NE OBRAČUNAVAJO SKUPAJ S PLAČAMI",False,gd_chart 257000,gd_acc_257000,account.data_account_type_current_liabilities,"KRATKOROČNE OBVEZNOSTI ZA DAVEK IZ DRUGIH PREJEMKOV IZ DELOVNEGA RAZMERJA, KI SE NE OBRAČUNAVAJO SKUPAJ S PLAČAMI",False,gd_chart -260001,gd_acc_260001,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 8,5%",False,gd_chart -260002,gd_acc_260002,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 20%",False,gd_chart -260003,gd_acc_260003,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 8,5% V EU",False,gd_chart -260004,gd_acc_260004,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 20% V EU",False,gd_chart -260005,gd_acc_260005,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 8,5% IZVEN EU",False,gd_chart -260006,gd_acc_260006,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 20% IZVEN EU",False,gd_chart +260001,gd_acc_260001,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 9,5%",False,gd_chart +260002,gd_acc_260002,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 22%",False,gd_chart +260003,gd_acc_260003,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 9,5% V EU",False,gd_chart +260004,gd_acc_260004,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 22% V EU",False,gd_chart +260005,gd_acc_260005,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 9,5% IZVEN EU",False,gd_chart +260006,gd_acc_260006,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA ZARAČUNANI DDV 22% IZVEN EU",False,gd_chart 261000,gd_acc_261000,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA DDV, CARINO IN DRUGE DAJATVE OD UVOŽENEGA BLAGA",False,gd_chart 262000,gd_acc_262000,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA PRISPEVKE IZPLAČEVALCA",False,gd_chart 263000,gd_acc_263000,account.data_account_type_current_liabilities,"OBVEZNOSTI ZA DAVEK OD IZPLAČANIH PLAČ",False,gd_chart diff --git a/addons/report/i18n/report.pot b/addons/report/i18n/report.pot index 8b66e8b4728..d35bf683eed 100644 --- a/addons/report/i18n/report.pot +++ b/addons/report/i18n/report.pot @@ -507,6 +507,12 @@ msgid "Unable to find Wkhtmltopdf on this \n" "wkhtmltopdf.org" msgstr "" +#. module: report +#: code:addons/report/models/report.py:164 +#, python-format +msgid "Unable to find Wkhtmltopdf on this system. The PDF can not be created." +msgstr "" + #. module: report #: model:ir.ui.view,arch_db:report.external_layout_footer msgid "Website:" @@ -542,17 +548,14 @@ msgid "base.config.settings" msgstr "" #. module: report -#: model:ir.model,name:report.model_ir_actions_report_xml -msgid "ir.actions.report.xml" +#. openerp-web +#: code:addons/report/static/src/js/qwebactionmanager.js:96 +#, python-format +msgid "Your installation of Wkhtmltopdf seems to be broken. The \n" +"report will be shown in html.

wkhtmltopdf.org" msgstr "" #. module: report #: model:ir.model,name:report.model_ir_qweb_field_barcode msgid "ir.qweb.field.barcode" msgstr "" - -#. module: report -#: model:ir.model,name:report.model_report_abstract_report -msgid "report.abstract_report" -msgstr "" - diff --git a/addons/report/models/report.py b/addons/report/models/report.py index 2eed9824bf2..6f01e3d26f2 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -59,16 +59,21 @@ except (OSError, IOError): else: _logger.info('Will use the Wkhtmltopdf binary at %s' % _get_wkhtmltopdf_bin()) out, err = process.communicate() - version = re.search('([0-9.]+)', out or "0").group(0) - if LooseVersion(version) < LooseVersion('0.12.0'): - _logger.info('Upgrade Wkhtmltopdf to (at least) 0.12.0') - wkhtmltopdf_state = 'upgrade' - else: - wkhtmltopdf_state = 'ok' + match = re.search('([0-9.]+)', out) + if match: + version = match.group(0) + if LooseVersion(version) < LooseVersion('0.12.0'): + _logger.info('Upgrade Wkhtmltopdf to (at least) 0.12.0') + wkhtmltopdf_state = 'upgrade' + else: + wkhtmltopdf_state = 'ok' - if config['workers'] == 1: - _logger.info('You need to start Odoo with at least two workers to print a pdf version of the reports.') - wkhtmltopdf_state = 'workers' + if config['workers'] == 1: + _logger.info('You need to start Odoo with at least two workers to print a pdf version of the reports.') + wkhtmltopdf_state = 'workers' + else: + _logger.info('Wkhtmltopdf seems to be broken.') + wkhtmltopdf_state = 'broken' class Report(models.Model): @@ -141,6 +146,14 @@ class Report(models.Model): def get_pdf(self, docids, report_name, html=None, data=None): """This method generates and returns pdf version of a report. """ + + if self._check_wkhtmltopdf() == 'install': + # wkhtmltopdf is not installed + # the call should be catched before (cf /report/check_wkhtmltopdf) but + # if get_pdf is called manually (email template), the check could be + # bypassed + raise UserError(_("Unable to find Wkhtmltopdf on this system. The PDF can not be created.")) + # As the assets are generated during the same transaction as the rendering of the # templates calling them, there is a scenario where the assets are unreachable: when # you make a request to read the assets while the transaction creating them is not done. diff --git a/addons/report/static/src/js/qwebactionmanager.js b/addons/report/static/src/js/qwebactionmanager.js index f67357e926a..4bd7b8c89ea 100644 --- a/addons/report/static/src/js/qwebactionmanager.js +++ b/addons/report/static/src/js/qwebactionmanager.js @@ -18,6 +18,7 @@ var WKHTMLTOPDF_MESSAGES = { 'install': _lt('Unable to find Wkhtmltopdf on this \nsystem. The report will be shown in html.

\nwkhtmltopdf.org'), 'workers': _lt('You need to start OpenERP with at least two \nworkers to print a pdf version of the reports.'), 'upgrade': _lt('You should upgrade your version of\nWkhtmltopdf to at least 0.12.0 in order to get a correct display of headers and footers as well as\nsupport for table-breaking between pages.

wkhtmltopdf.org'), + 'broken': _lt('Your installation of Wkhtmltopdf seems to be broken. The report will be shown in html.

wkhtmltopdf.org') }; var trigger_download = function (session, response, c, action, options) { diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 4b41e10361b..b5512c6d6e0 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -697,7 +697,6 @@ class QuantPackage(models.Model): # TDE FIXME: why superuser ? package.mapped('quant_ids').sudo().write({'package_id': package.parent_id.id}) package.mapped('children_ids').write({'parent_id': package.parent_id.id}) - self.unlink() return self.env['ir.actions.act_window'].for_xml_id('stock', 'action_package_view') @api.multi diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 189618a7755..aa386c6b8b7 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -791,7 +791,7 @@ class Orderpoint(models.Model): ('qty_multiple_check', 'CHECK( qty_multiple >= 0 )', 'Qty Multiple must be greater than or equal to zero.'), ] - @api.constrains('product_id', 'product_uom') + @api.constrains('product_id') def _check_product_uom(self): ''' Check if the UoM has the same category as the product standard UoM ''' if any(orderpoint.product_id.uom_id.category_id != orderpoint.product_uom.category_id for orderpoint in self): diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml index 579b38b3995..5511e1de8f5 100644 --- a/addons/stock/report/report_deliveryslip.xml +++ b/addons/stock/report/report_deliveryslip.xml @@ -12,7 +12,7 @@ Customer Address:
-
diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 8d05a92260c..9c6f1df5e8c 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -340,7 +340,7 @@ var FieldTextHtml = widget.extend({ $("body").toggleClass("o_form_FieldTextHtml_fullscreen"); var full = $("body").hasClass("o_form_FieldTextHtml_fullscreen"); self.$iframe.parents().toggleClass('o_form_fullscreen_ancestor', full); - self.resize(); + $(window).trigger("resize"); // induce a resize() call and let other backend elements know (the navbar extra items management relies on this) }); this.$body.on('click', '[data-action="cancel"]', function (event) { diff --git a/doc/cla/corporate/VanRoey.md b/doc/cla/corporate/VanRoey.md index 956d6bb43a4..0e3122e27f4 100644 --- a/doc/cla/corporate/VanRoey.md +++ b/doc/cla/corporate/VanRoey.md @@ -13,6 +13,4 @@ Yenthe Van Ginneken yenthe.vanginneken@vanroey.be https://github.com/Yenthe666 List of contributors: Yenthe Van Ginneken yenthe.vanginneken@vanroey.be https://github.com/Yenthe666 -Maarten Wijnants maarten.wijnants@vanroey.be https://github.com/weilant Vincent Adriaensen vincent.adriaensen@vanroey.be https://github.com/vincentadriaensen -Pieter Paulussen pieter.paulussen@vanroey.be https://github.com/PieterPaulussen diff --git a/doc/cla/corporate/goopen.md b/doc/cla/corporate/goopen.md index 91f5cded22a..5d562fed621 100644 --- a/doc/cla/corporate/goopen.md +++ b/doc/cla/corporate/goopen.md @@ -12,4 +12,5 @@ Aleš Ferlan alefer89@protonmail.com List of contributors: +Aleš Ferlan alefer89@gmail.com https://github.com/Alko89 Aleš Ferlan alefer89@protonmail.com diff --git a/doc/cla/individual/Sergio2409.md b/doc/cla/individual/Sergio2409.md new file mode 100644 index 00000000000..dd118d31abf --- /dev/null +++ b/doc/cla/individual/Sergio2409.md @@ -0,0 +1,11 @@ +Cuba, 2016-10-24 + +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, + +Sergio Valdés sergiov@merchise.org https://github.com/Sergio2409 diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py index fa7ca55c200..893eccd8d01 100644 --- a/odoo/addons/base/ir/ir_ui_view.py +++ b/odoo/addons/base/ir/ir_ui_view.py @@ -271,7 +271,7 @@ actual arch. self.raise_view_error(message, self.id) return True - @api.constrains('arch', 'arch_base') + @api.constrains('arch_db') def _check_xml(self): # Sanity checks: the view should not break anything upon rendering! # Any exception raised below will cause a transaction rollback. diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 5dbfbdf4f46..84b77d5852d 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -130,6 +130,26 @@ class TestFields(common.TransactionCase): }) check_stored(discussion3) + def test_11_computed_access(self): + """ test computed fields with access right errors """ + User = self.env['res.users'] + user1 = User.create({'name': 'Aaaah', 'login': 'a'}) + user2 = User.create({'name': 'Boooh', 'login': 'b'}) + user3 = User.create({'name': 'Crrrr', 'login': 'c'}) + # add a rule to not give access to user2 + self.env['ir.rule'].create({ + 'model_id': self.env['ir.model'].search([('model', '=', 'res.users')]).id, + 'domain_force': "[('id', '!=', %d)]" % user2.id, + }) + # group users as a recordset, and read them as user demo + users = (user1 + user2 + user3).sudo(self.env.ref('base.user_demo')) + user1, user2, user3 = users + # regression test: a bug invalidated the field's value from cache + user1.company_type + with self.assertRaises(AccessError): + user2.company_type + user3.company_type + def test_12_recursive(self): """ test recursively dependent fields """ Category = self.env['test_new_api.category'] diff --git a/odoo/fields.py b/odoo/fields.py index 8971c239ac9..72e82249d54 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -922,15 +922,15 @@ class Field(object): for field in fields: for record in records: record._cache[field] = field.convert_to_cache(False, record, validate=False) - with records.env.protecting(fields, records): - if isinstance(self.compute, basestring): - getattr(records, self.compute)() - else: - self.compute(records) + if isinstance(self.compute, basestring): + getattr(records, self.compute)() + else: + self.compute(records) def compute_value(self, records): """ Invoke the compute method on ``records``; the results are in cache. """ - with records.env.do_in_draft(): + fields = records._field_computed[self] + with records.env.do_in_draft(), records.env.protecting(fields, records): try: self._compute_value(records) except (AccessError, MissingError): diff --git a/odoo/models.py b/odoo/models.py index c8287c3f9f9..3ac08b0f4bd 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -664,8 +664,11 @@ class BaseModel(object): cls = type(self) methods = [] for attr, func in getmembers(cls, is_constraint): - if not all(name in cls._fields for name in func._constrains): - _logger.warning("@constrains%r parameters must be field names", func._constrains) + for name in func._constrains: + if name not in cls._fields: + _logger.warning("method %s.%s: @constrains parameter %r is not a field name", cls._name, attr, name) + if not cls._fields[name].store: + _logger.warning("method %s.%s: @constrains parameter %r is not stored", cls._name, attr, name) methods.append(func) # optimization: memoize result on cls, it will not be recomputed