[MERGE] forward port branch saas-16 up to 98d01e46e5

This commit is contained in:
Christophe Simonis
2018-02-20 11:53:06 +01:00
15 changed files with 123 additions and 56 deletions
+1 -1
View File
@@ -434,7 +434,7 @@ class AlarmManager(models.AbstractModel):
result = False
if alarm.type == 'email':
result = meeting.attendee_ids._send_mail_to_attendees('calendar.calendar_template_meeting_reminder', force_send=True)
result = meeting.attendee_ids.filtered(lambda r: r.state != 'declined')._send_mail_to_attendees('calendar.calendar_template_meeting_reminder', force_send=True)
return result
def do_notif_reminder(self, alert):
+1 -1
View File
@@ -317,7 +317,7 @@ class EventEvent(models.Model):
self.state = 'confirm'
@api.one
def mail_attendees(self, template_id, force_send=False, filter_func=lambda self: True):
def mail_attendees(self, template_id, force_send=False, filter_func=lambda self: self.state != 'cancel'):
for attendee in self.registration_ids.filtered(filter_func):
self.env['mail.template'].browse(template_id).send_mail(attendee.id, force_send=force_send)
+7 -2
View File
@@ -218,7 +218,12 @@ class Employee(models.Model):
def write(self, values):
res = super(Employee, self).write(values)
if 'parent_id' in values:
if 'parent_id' in values or 'department_id' in values:
holidays = self.env['hr.holidays'].search([('state', 'in', ['draft', 'confirm']), ('employee_id', 'in', self.ids)])
holidays.write({'manager_id': values['parent_id']})
hr_vals = {}
if values.get('parent_id') is not None:
hr_vals['manager_id'] = values['parent_id']
if values.get('department_id') is not None:
hr_vals['department_id'] = values['department_id']
holidays.write(hr_vals)
return res
+2 -1
View File
@@ -197,7 +197,7 @@ class Holidays(models.Model):
"\nChoose 'Allocation Request' if you want to increase the number of leaves available for someone")
parent_id = fields.Many2one('hr.holidays', string='Parent')
linked_request_ids = fields.One2many('hr.holidays', 'parent_id', string='Linked Requests')
department_id = fields.Many2one('hr.department', related='employee_id.department_id', string='Department', readonly=True, store=True)
department_id = fields.Many2one('hr.department', string='Department', readonly=True)
category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee')
holiday_type = fields.Selection([
@@ -216,6 +216,7 @@ class Holidays(models.Model):
@api.onchange('employee_id')
def _onchange_employee_id(self):
self.manager_id = self.employee_id and self.employee_id.parent_id
self.department_id = self.employee_id and self.employee_id.department_id
@api.multi
@api.depends('number_of_days_temp', 'type')
@@ -545,7 +545,6 @@ result = -contract.car_atn
ref('hr_salary_rule_representation_fees'),
ref('hr_payroll_rules_suppl_net'),
ref('hr_payroll_rules_retained_net'),
ref('hr_salary_rule_commission_on_target'),
ref('hr_salary_rule_pp_reduction_employees'),
])]" name="rule_ids"/>
@@ -571,7 +570,6 @@ result = -contract.car_atn
ref('hr_salary_rule_representation_fees'),
ref('hr_payroll_rules_suppl_net'),
ref('hr_payroll_rules_retained_net'),
ref('hr_salary_rule_commission_on_target'),
ref('hr_salary_rule_pp_reduction_workers')
])]" name="rule_ids"/>
<field name="company_id" ref="base.main_company"/>
@@ -116,8 +116,8 @@ class HrContract(models.Model):
@api.depends('commission_on_target')
def _compute_warrants_cost(self):
for contract in self:
contract.warrants_cost = contract.commission_on_target * 1.326 / 1.05 * 12.0
contract.warrant_value_employee = contract.warrants_cost * 0.54
contract.warrants_cost = contract.commission_on_target * 1.05 * 12.0
contract.warrant_value_employee = contract.commission_on_target * (1.00 - 0.535) * 12.0
@api.depends('wage', 'fuel_card', 'representation_fees', 'transport_employer_cost',
'internet', 'mobile', 'mobile_plus')
+1 -1
View File
@@ -232,7 +232,7 @@ class CrmTeam(models.Model):
values[index][y_field] = data_item.get('y_value')
elif self.dashboard_graph_group == 'week':
weeks_in_start_year = int(date(start_date.year, 12, 31).isocalendar()[1])
weeks_in_start_year = int(date(start_date.year, 12, 28).isocalendar()[1]) # This date is always in the last week of ISO years
for week in range(0, (end_date.isocalendar()[1] - start_date.isocalendar()[1]) % weeks_in_start_year + 1):
short_name = get_week_name(start_date + relativedelta(days=7 * week), locale)
values.append({x_field: short_name, y_field: 0})
+1 -1
View File
@@ -1382,7 +1382,7 @@ class ExportFormat(object):
model, fields, ids, domain, import_compat = \
operator.itemgetter('model', 'fields', 'ids', 'domain', 'import_compat')(params)
Model = request.env[model].with_context(**params.get('context', {}))
Model = request.env[model].with_context(import_compat=import_compat, **params.get('context', {}))
records = Model.browse(ids) or Model.search(domain, offset=0, limit=False, order=False)
if not Model._is_an_ordinary_table():
+6 -3
View File
@@ -116,13 +116,16 @@
</xpath>
<xpath expr="//t[@t-set='head_website']" position="replace">
<t t-set="head_website">
<meta name="generator" content="Odoo"/>
<meta t-if="main_object and 'website_indexed' in main_object
and not main_object.website_indexed" name="robots" content="noindex"/>
<meta name="description" t-att-content="main_object and 'website_meta_description' in main_object
<t t-set="meta_description" t-value="main_object and 'website_meta_description' in main_object
and main_object.website_meta_description or website_meta_description"/>
<meta name="keywords" t-att-content="main_object and 'website_meta_keywords' in main_object
<t t-set="meta_keywords" t-value="main_object and 'website_meta_keywords' in main_object
and main_object.website_meta_keywords or website_meta_keywords"/>
<meta name="generator" content="Odoo"/>
<meta t-if="meta_description" name="description" t-att-content="meta_description"/>
<meta t-if="meta_keywords" name="keywords" t-att-content="meta_keywords"/>
<!-- OpenGraph tags for Facebook sharing -->
<meta property="og:title" t-att-content="additional_title" />
+2 -2
View File
@@ -23,8 +23,8 @@ class Event(models.Model):
website_menu = fields.Boolean(
'Dedicated Menu', compute='_compute_website_menu', inverse='_set_website_menu',
help="Creates menus Introduction, Location and Register on the page "
" of the event on the website.", store=True, copy=False)
menu_id = fields.Many2one('website.menu', 'Event Menu')
" of the event on the website.", store=True)
menu_id = fields.Many2one('website.menu', 'Event Menu', copy=False)
def _compute_is_participating(self):
# we don't allow public user to see participating label
+10
View File
@@ -0,0 +1,10 @@
Norway, 2018-02-08
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,
Marius Stedjan marius@stedjan.com https://github.com/willmakenoise
+2 -2
View File
@@ -54,7 +54,7 @@ Running the server
Maximum allowed virtual memory per worker. If the limit is exceeded,
the worker is killed and recycled at the end of the current request.
Defaults to 640MB.
Defaults to 2048MB.
.. option:: --limit-memory-hard <limit>
@@ -62,7 +62,7 @@ Running the server
immediately killed without waiting for the end of the current request
processing.
Defaults to 768MB.
Defaults to 2560MB.
.. option:: --limit-time-cpu <limit>
+48 -1
View File
@@ -601,7 +601,54 @@ class test_m2m(CreatorCase):
['', u'export.many2many.other:13'],
])
# essentially same as o2m, so boring
def test_multiple_records_subfield(self):
r = self.make(self.commands)
xid = self.env['ir.model.data'].create({
'name': 'whopwhopwhop',
'module': '__t__',
'model': r._name,
'res_id': r.id,
}).complete_name
sids = [
self.env['ir.model.data'].create({
'name': sub.str,
'module': '__t__',
'model': sub._name,
'res_id': sub.id,
}).complete_name
for sub in r.value
]
r.invalidate_cache()
self.assertEqual(
r._export_rows([['value', 'id']]),
[['__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']]
)
self.assertEqual(
r.with_context(import_compat=True)._export_rows([['value', 'id']]),
[['__t__.record000,__t__.record001,__t__.record010,__t__.record011,__t__.record100']]
)
self.assertEqual(
r.with_context(import_compat=False)._export_rows([['id'], ['value', 'id'], ['value', 'value']]),
[
[xid, u'__t__.record000', u'4'],
[u'', u'__t__.record001', u'42'],
[u'', u'__t__.record010', u'36'],
[u'', u'__t__.record011', u'4'],
[u'', u'__t__.record100', u'13']
]
)
self.assertEqual(
r.with_context(import_compat=False)._export_rows([['id'], ['value', 'value'], ['value', 'id']]),
[
[xid, u'4', u'__t__.record000'],
[u'', u'42', u'__t__.record001'],
[u'', u'36', u'__t__.record010'],
[u'', u'4', u'__t__.record011'],
[u'', u'13', u'__t__.record100']
]
)
class test_function(CreatorCase):
+39 -36
View File
@@ -650,6 +650,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
:param fields: list of lists of fields to traverse
:return: list of lists of corresponding values
"""
import_compatible = self.env.context.get('import_compat', True)
lines = []
def splittor(rs):
@@ -696,8 +697,9 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
else:
primary_done.append(name)
# This is a special case, its strange behavior is intended!
if field.type == 'many2many' and len(path) > 1 and path[1] == 'id':
# in import_compat mode, m2m should always be exported as
# a comma-separated list of xids in a single cell
if import_compatible and field.type == 'many2many' and len(path) > 1 and path[1] == 'id':
xml_ids = [r.__export_xml_id() for r in value]
current[i] = ','.join(xml_ids) or False
continue
@@ -2853,47 +2855,48 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
raise UserError(_('Unable to delete this document because it is used as a default property'))
# Delete the records' properties.
self.env['ir.property'].search([('res_id', 'in', refs)]).unlink()
with self.env.norecompute():
self.env['ir.property'].search([('res_id', 'in', refs)]).unlink()
self.check_access_rule('unlink')
self.check_access_rule('unlink')
cr = self._cr
Data = self.env['ir.model.data'].sudo().with_context({})
Defaults = self.env['ir.default'].sudo()
Attachment = self.env['ir.attachment']
cr = self._cr
Data = self.env['ir.model.data'].sudo().with_context({})
Defaults = self.env['ir.default'].sudo()
Attachment = self.env['ir.attachment']
for sub_ids in cr.split_for_in_conditions(self.ids):
query = "DELETE FROM %s WHERE id IN %%s" % self._table
cr.execute(query, (sub_ids,))
for sub_ids in cr.split_for_in_conditions(self.ids):
query = "DELETE FROM %s WHERE id IN %%s" % self._table
cr.execute(query, (sub_ids,))
# Removing the ir_model_data reference if the record being deleted
# is a record created by xml/csv file, as these are not connected
# with real database foreign keys, and would be dangling references.
#
# Note: the following steps are performed as superuser to avoid
# access rights restrictions, and with no context to avoid possible
# side-effects during admin calls.
data = Data.search([('model', '=', self._name), ('res_id', 'in', sub_ids)])
if data:
data.unlink()
# Removing the ir_model_data reference if the record being deleted
# is a record created by xml/csv file, as these are not connected
# with real database foreign keys, and would be dangling references.
#
# Note: the following steps are performed as superuser to avoid
# access rights restrictions, and with no context to avoid possible
# side-effects during admin calls.
data = Data.search([('model', '=', self._name), ('res_id', 'in', sub_ids)])
if data:
data.unlink()
# For the same reason, remove the defaults having some of the
# records as value
Defaults.discard_records(self.browse(sub_ids))
# For the same reason, remove the defaults having some of the
# records as value
Defaults.discard_records(self.browse(sub_ids))
# For the same reason, remove the relevant records in ir_attachment
# (the search is performed with sql as the search method of
# ir_attachment is overridden to hide attachments of deleted
# records)
query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s'
cr.execute(query, (self._name, sub_ids))
attachments = Attachment.browse([row[0] for row in cr.fetchall()])
if attachments:
attachments.unlink()
# For the same reason, remove the relevant records in ir_attachment
# (the search is performed with sql as the search method of
# ir_attachment is overridden to hide attachments of deleted
# records)
query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s'
cr.execute(query, (self._name, sub_ids))
attachments = Attachment.browse([row[0] for row in cr.fetchall()])
if attachments:
attachments.unlink()
# invalidate the *whole* cache, since the orm does not handle all
# changes made in the database, like cascading delete!
self.invalidate_cache()
# invalidate the *whole* cache, since the orm does not handle all
# changes made in the database, like cascading delete!
self.invalidate_cache()
# recompute new-style fields
if self.env.recompute and self._context.get('recompute', True):
+1 -1
View File
@@ -41,7 +41,7 @@ RPC_FAULT_CODE_ACCESS_ERROR = 4
def xmlrpc_handle_exception_int(e):
if isinstance(e, odoo.exceptions.UserError):
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, odoo.tools.ustr(e.value))
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, odoo.tools.ustr(e.name))
elif isinstance(e, odoo.exceptions.RedirectWarning):
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e))
elif isinstance(e, odoo.exceptions.MissingError):