[MERGE] forward port branch saas-15 up to c0f24d54cf
This commit is contained in:
@@ -344,7 +344,7 @@ class AccountMoveLine(models.Model):
|
||||
#computing the `reconciled` field.
|
||||
reconciled = False
|
||||
digits_rounding_precision = line.company_id.currency_id.rounding
|
||||
if float_is_zero(amount, precision_rounding=digits_rounding_precision):
|
||||
if (line.matched_debit_ids or line.matched_credit_ids) and float_is_zero(amount, precision_rounding=digits_rounding_precision):
|
||||
if line.currency_id and line.amount_currency:
|
||||
if float_is_zero(amount_residual_currency, precision_rounding=line.currency_id.rounding):
|
||||
reconciled = True
|
||||
@@ -1052,7 +1052,7 @@ class AccountMoveLine(models.Model):
|
||||
all_accounts.append(line.account_id)
|
||||
if (line.account_id.internal_type in ('receivable', 'payable')):
|
||||
partners.add(line.partner_id.id)
|
||||
if line.reconciled:
|
||||
if (line.matched_debit_ids or line.matched_credit_ids) and line.reconciled:
|
||||
raise UserError(_('You are trying to reconcile some entries that are already reconciled!'))
|
||||
if len(company_ids) > 1:
|
||||
raise UserError(_('To reconcile the entries company should be the same for all entries!'))
|
||||
|
||||
@@ -132,6 +132,11 @@ msgstr ""
|
||||
msgid "No file sent."
|
||||
msgstr ""
|
||||
|
||||
#. module: base_import_module
|
||||
#: model:ir.ui.view,arch_db:base_import_module.view_base_module_import
|
||||
msgid "Note: you can only import data modules (.xml files and static assets)"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_import_module
|
||||
#: code:addons/base_import_module/controllers/main.py:25
|
||||
#, python-format
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<form string="Import module">
|
||||
<field name="state" invisible="1"/>
|
||||
<separator string="Import Module" colspan="4"/>
|
||||
<p class="alert alert-warning">Note: you can only import data modules (.xml files and static assets)</p>
|
||||
<group states="init" col="4">
|
||||
<label string="Select module package to import (.zip file):" colspan="4"/>
|
||||
<field name="module_file" colspan="4"/>
|
||||
|
||||
@@ -441,7 +441,9 @@ class GoogleCalendar(models.AbstractModel):
|
||||
if google_attendee.get('found'):
|
||||
continue
|
||||
|
||||
attendee = ResPartner.search([('email', '=', google_attendee['email'])], limit=1)
|
||||
attendee = ResPartner.search([('email', '=ilike', google_attendee['email']), ('user_ids', '!=', False)], limit=1)
|
||||
if not attendee:
|
||||
attendee = ResPartner.search([('email', '=ilike', google_attendee['email'])], limit=1)
|
||||
if not attendee:
|
||||
data = {
|
||||
'email': partner_email,
|
||||
|
||||
@@ -17,7 +17,7 @@ Piano dei conti italiano di un'impresa generica.
|
||||
Italian accounting chart and localization.
|
||||
""",
|
||||
'category': 'Localization',
|
||||
'website': 'http://www.openerp-italia.org/',
|
||||
'website': 'http://www.odoo.com/',
|
||||
'data': [
|
||||
'data/l10n_it_chart_data.xml',
|
||||
'data/account.account.template.csv',
|
||||
|
||||
@@ -486,13 +486,11 @@ class PosOrder(models.Model):
|
||||
aml = order.statement_ids.mapped('journal_entry_ids') | order.account_move.line_ids | order.invoice_id.move_id.line_ids
|
||||
aml = aml.filtered(lambda r: not r.reconciled and r.account_id.internal_type == 'receivable' and r.partner_id == order.partner_id.commercial_partner_id)
|
||||
|
||||
# Reconcile returns first
|
||||
# to avoid mixing up the credit of a payment and the credit of a return
|
||||
# in the receivable account
|
||||
aml_returns = aml.filtered(lambda l: (l.journal_id.type == 'sale' and l.credit) or (l.journal_id.type != 'sale' and l.debit))
|
||||
try:
|
||||
aml_returns.reconcile()
|
||||
(aml - aml_returns).reconcile()
|
||||
# Cash returns will be well reconciled
|
||||
# Whereas freight returns won't be
|
||||
# "c'est la vie..."
|
||||
aml.reconcile()
|
||||
except Exception:
|
||||
# There might be unexpected situations where the automatic reconciliation won't
|
||||
# work. We don't want the user to be blocked because of this, since the automatic
|
||||
|
||||
@@ -260,7 +260,9 @@ class Product(models.Model):
|
||||
|
||||
# TODO: Still optimization possible when searching virtual quantities
|
||||
ids = []
|
||||
for product in self.with_context(prefetch_fields=False).search([]):
|
||||
# Order the search on `id` to prevent the default order on the product name which slows
|
||||
# down the search because of the join on the translation table to get the translated names.
|
||||
for product in self.with_context(prefetch_fields=False).search([], order='id'):
|
||||
if OPERATORS[operator](product[field], value):
|
||||
ids.append(product.id)
|
||||
return [('id', 'in', ids)]
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
<field name="use_existing_lots" groups="stock.group_production_lot"/>
|
||||
</group>
|
||||
<group string="Locations" groups="stock.group_stock_multi_locations">
|
||||
<field name="default_location_src_id" attrs="{'required': [('code', '=', 'internal')]}"/>
|
||||
<field name="default_location_dest_id" attrs="{'required': [('code', 'in', ('internal', 'incoming'))]}"/>
|
||||
<field name="default_location_src_id" attrs="{'required': [('code', '=', 'internal')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
|
||||
<field name="default_location_dest_id" attrs="{'required': [('code', 'in', ('internal', 'incoming'))]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
@@ -245,8 +245,8 @@
|
||||
<button name="action_assign_partner" string="Assign Partner" type="object" help="Assign the partner to the Operations. Use only if the partner is manually changed!"
|
||||
attrs="{'invisible': ['|', '|', ('move_line_exist', '=', False), ('state', 'not in', ('draft', 'assigned', 'confirmed')), ('picking_type_code', '=', 'internal')]}"
|
||||
class="oe_link oe_edit_only"/>
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
|
||||
<field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}"/>
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
|
||||
<field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
|
||||
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Lithuania, 2019-02-09
|
||||
|
||||
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,
|
||||
|
||||
Naglis Jonaitis naglis@mailbox.org https://github.com/naglis
|
||||
@@ -281,6 +281,16 @@ class TestFields(common.TransactionCase):
|
||||
self.assertEqual(record.bar, 'Ho')
|
||||
self.assertEqual(record.counts, {'compute': 0, 'inverse': 1})
|
||||
|
||||
def test_13_inverse_access(self):
|
||||
""" test access rights on inverse fields """
|
||||
foo = self.env['test_new_api.category'].create({'name': 'Foo'})
|
||||
user = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'})
|
||||
self.assertFalse(user.has_group('base.group_system'))
|
||||
# add group on non-stored inverse field
|
||||
self.patch(type(foo).display_name, 'groups', 'base.group_system')
|
||||
with self.assertRaises(AccessError):
|
||||
foo.sudo(user).display_name = 'Forbidden'
|
||||
|
||||
def test_14_search(self):
|
||||
""" test search on computed fields """
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
@@ -586,6 +596,10 @@ class TestFields(common.TransactionCase):
|
||||
self.env['ir.property'].create({'name': 'foo', 'fields_id': field_tag_id.id,
|
||||
'value': tag0, 'type': 'many2one'})
|
||||
|
||||
# assumption: users don't have access to 'ir.property'
|
||||
accesses = self.env['ir.model.access'].search([('model_id.model', '=', 'ir.property')])
|
||||
accesses.write(dict.fromkeys(['perm_read', 'perm_write', 'perm_create', 'perm_unlink'], False))
|
||||
|
||||
# create/modify a record, and check the value for each user
|
||||
record = self.env['test_new_api.company'].create({
|
||||
'foo': 'main',
|
||||
@@ -639,6 +653,13 @@ class TestFields(common.TransactionCase):
|
||||
self.assertEqual(record.sudo(user1).foo, False)
|
||||
self.assertEqual(record.sudo(user2).foo, 'default')
|
||||
|
||||
# set field with 'force_company' in context
|
||||
record.sudo(user0).with_context(force_company=company1.id).foo = 'beta'
|
||||
record.invalidate_cache()
|
||||
self.assertEqual(record.sudo(user0).foo, 'main')
|
||||
self.assertEqual(record.sudo(user1).foo, 'beta')
|
||||
self.assertEqual(record.sudo(user2).foo, 'default')
|
||||
|
||||
# create company record and attribute
|
||||
company_record = self.env['test_new_api.company'].create({'foo': 'ABC'})
|
||||
attribute_record = self.env['test_new_api.company.attr'].create({
|
||||
@@ -658,6 +679,25 @@ class TestFields(common.TransactionCase):
|
||||
self.assertEqual(attribute_record.bar, 'DEFDEF')
|
||||
self.assertFalse(self.env.has_todo())
|
||||
|
||||
# add group on company-dependent field
|
||||
self.assertFalse(user0.has_group('base.group_system'))
|
||||
self.patch(type(record).foo, 'groups', 'base.group_system')
|
||||
with self.assertRaises(AccessError):
|
||||
record.sudo(user0).foo = 'forbidden'
|
||||
|
||||
user0.write({'groups_id': [(4, self.env.ref('base.group_system').id)]})
|
||||
record.sudo(user0).foo = 'yes we can'
|
||||
|
||||
# add ir.rule to prevent access on record
|
||||
self.assertTrue(user0.has_group('base.group_user'))
|
||||
rule = self.env['ir.rule'].create({
|
||||
'model_id': self.env['ir.model']._get_id(record._name),
|
||||
'groups': [self.env.ref('base.group_user').id],
|
||||
'domain_force': str([('id', '!=', record.id)]),
|
||||
})
|
||||
with self.assertRaises(AccessError):
|
||||
record.sudo(user0).foo = 'forbidden'
|
||||
|
||||
def test_30_read(self):
|
||||
""" test computed fields as returned by read(). """
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
|
||||
+14
-2
@@ -622,13 +622,25 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
return model.env['ir.property'].get(self.name, self.model_name)
|
||||
|
||||
def _compute_company_dependent(self, records):
|
||||
Property = records.env['ir.property']
|
||||
# read property as superuser, as the current user may not have access
|
||||
context = records.env.context
|
||||
if 'force_company' not in context:
|
||||
field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name)
|
||||
company = records.env['res.company']._company_default_get(self.model_name, field_id)
|
||||
context = dict(context, force_company=company.id)
|
||||
Property = records.env(user=SUPERUSER_ID, context=context)['ir.property']
|
||||
values = Property.get_multi(self.name, self.model_name, records.ids)
|
||||
for record in records:
|
||||
record[self.name] = values.get(record.id)
|
||||
|
||||
def _inverse_company_dependent(self, records):
|
||||
Property = records.env['ir.property']
|
||||
# update property as superuser, as the current user may not have access
|
||||
context = records.env.context
|
||||
if 'force_company' not in context:
|
||||
field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name)
|
||||
company = records.env['res.company']._company_default_get(self.model_name, field_id)
|
||||
context = dict(context, force_company=company.id)
|
||||
Property = records.env(user=SUPERUSER_ID, context=context)['ir.property']
|
||||
values = {
|
||||
record.id: self.convert_to_write(record[self.name], record)
|
||||
for record in records
|
||||
|
||||
+6
-1
@@ -3102,10 +3102,15 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
protected_fields = [self._fields[n] for n in new_vals]
|
||||
with self.env.protecting(protected_fields, self):
|
||||
# write old-style fields with (low-level) method _write
|
||||
if old_vals:
|
||||
if old_vals or new_vals:
|
||||
# if log_access is enabled, this updates 'write_date' and
|
||||
# 'write_uid' and check access rules, even when old_vals is
|
||||
# empty
|
||||
self._write(old_vals)
|
||||
|
||||
if new_vals:
|
||||
self.check_field_access_rights('write', list(new_vals))
|
||||
|
||||
self.modified(set(new_vals) - set(old_vals))
|
||||
|
||||
# put the values of fields into cache, and inverse them
|
||||
|
||||
Reference in New Issue
Block a user