[MERGE] forward port branch saas-16 up to 98d01e46e5
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user