def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): """Override read_group to add res_field=False in domain if not present.""" if not fields: raise AccessError( _("Sorry, you must provide fields to read on attachments")) if any('(' in field for field in fields + groupby): raise AccessError( _("Sorry, the syntax 'name:agg(field)' is not available for attachments" )) if not any(item[0] in ('id', 'res_field') for item in domain): domain.insert(0, ('res_field', '=', False)) groupby = [groupby] if isinstance(groupby, str) else groupby allowed_fields = self._read_group_allowed_fields() fields_set = set(field.split(':')[0] for field in fields + groupby) if not self.env.is_system() and ( not fields or fields_set.difference(allowed_fields)): raise AccessError( _("Sorry, you are not allowed to access these fields on attachments." )) return super().read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
def migrate_to_lobject(self): """migrates all binary attachments to postgres large object storage""" if not self.env.user._is_admin(): raise AccessError( _('Only administrators can execute this action.')) atts = self.search([ '&', '&', ('id', '>', 0), # bypass filtering of field-attached attachments ('type', '=', 'binary'), '|', ('store_fname', '=', False), ('store_fname', 'not like', LARGE_OBJECT_LOCATION + ':%') ]) att_count = len(atts) if att_count: log.info( f'Migrating {att_count} database attachments to Large Objects...' ) if self._storage() != LARGE_OBJECT_LOCATION: raise Exception( f'Default storage is not set to Large Object ({LARGE_OBJECT_LOCATION})' ) current_att = 1 for att in atts: log.info( f'Migrating attachment ID {att.id} ({current_att} of {att_count})...' ) # re-save data to move to lobject storage att.write({'mimetype': att.mimetype, 'datas': att.datas}) current_att += 1
def _compute_kpi_hr_recruitment_new_colleagues_value(self): if not self.env.user.has_group('hr_recruitment.group_hr_recruitment_user'): raise AccessError(_("Do not have access, skip this data for user's digest email")) for record in self: start, end, company = record._get_kpi_compute_parameters() new_colleagues = self.env['hr.employee'].search_count([ ('create_date', '>=', start), ('create_date', '<', end), ('company_id', '=', company.id) ]) record.kpi_hr_recruitment_new_colleagues_value = new_colleagues
def _parse_import_data_recursive(self, model, prefix, data, import_fields, options): # Get fields of type date/datetime all_fields = self.env[model].fields_get() for name, field in all_fields.items(): name = prefix + name if field['type'] in ('date', 'datetime') and name in import_fields: index = import_fields.index(name) self._parse_date_from_data(data, index, name, field['type'], options) # Check if the field is in import_field and is a relational (followed by /) # Also verify that the field name exactly match the import_field at the correct level. elif any(name + '/' in import_field and name == import_field.split('/')[prefix.count('/')] for import_field in import_fields): # Recursive call with the relational as new model and add the field name to the prefix self._parse_import_data_recursive(field['relation'], name + '/', data, import_fields, options) elif field['type'] in ('float', 'monetary') and name in import_fields: # Parse float, sometimes float values from file have currency symbol or () to denote a negative value # We should be able to manage both case index = import_fields.index(name) self._parse_float_from_data(data, index, name, options) elif field['type'] == 'binary' and field.get('attachment') and any( f in name for f in IMAGE_FIELDS) and name in import_fields: index = import_fields.index(name) with requests.Session() as session: session.stream = True for num, line in enumerate(data): if re.match( config.get("import_image_regex", DEFAULT_IMAGE_REGEX), line[index]): if not self.env.user._can_import_remote_urls(): raise AccessError( _("You can not import images via URL, check with your administrator or support for the reason." )) line[index] = self._import_image_by_url( line[index], session, name, num) else: try: base64.b64decode(line[index], validate=True) except binascii.Error: raise ValueError( _("Found invalid image data, images should be imported as either URLs or base64-encoded data." )) return data
def run(self): """ Runs the server action. For each server action, the run_action_<STATE> method is called. This allows easy overriding of the server actions. :param dict context: context should contain following keys - active_id: id of the current object (single mode) - active_model: current model that should equal the action's model The following keys are optional: - active_ids: ids of the current records (mass mode). If active_ids and active_id are present, active_ids is given precedence. :return: an action_id to be executed, or False is finished correctly without return action """ res = False for action in self: action_groups = action.groups_id if action_groups and not (action_groups & self.env.user.groups_id): raise AccessError( _("You don't have enough access rights to run this action." )) eval_context = self._get_eval_context(action) if hasattr(self, 'run_action_%s_multi' % action.state): # call the multi method run_self = self.with_context(eval_context['env'].context) func = getattr(run_self, 'run_action_%s_multi' % action.state) res = func(action, eval_context=eval_context) elif hasattr(self, 'run_action_%s' % action.state): active_id = self._context.get('active_id') if not active_id and self._context.get('onchange_self'): active_id = self._context['onchange_self']._origin.id if not active_id: # onchange on new record func = getattr(self, 'run_action_%s' % action.state) res = func(action, eval_context=eval_context) active_ids = self._context.get( 'active_ids', [active_id] if active_id else []) for active_id in active_ids: # run context dedicated to a particular active_id run_self = self.with_context(active_ids=[active_id], active_id=active_id) eval_context["env"].context = run_self._context # call the single method related to the action: run_action_<STATE> func = getattr(run_self, 'run_action_%s' % action.state) res = func(action, eval_context=eval_context) return res or False
def _compute_kpi_website_sale_total_value(self): if not self.env.user.has_group( 'sales_team.group_sale_salesman_all_leads'): raise AccessError( _("Do not have access, skip this data for user's digest email") ) for record in self: start, end, company = record._get_kpi_compute_parameters() confirmed_website_sales = self.env['sale.order'].search([ ('date_order', '>=', start), ('date_order', '<', end), ('state', 'not in', ['draft', 'cancel', 'sent']), ('website_id', '!=', False), ('company_id', '=', company.id) ]) record.kpi_website_sale_total_value = sum( confirmed_website_sales.mapped('amount_total'))
def force_storage(self): """Force all attachments to be stored in the currently configured storage""" if not self.env.is_admin(): raise AccessError( _('Only administrators can execute this action.')) # domain to retrieve the attachments to migrate domain = { 'db': [('store_fname', '!=', False)], 'file': [('db_datas', '!=', False)], }[self._storage()] for attach in self.search(domain): attach.write({'datas': attach.datas}) return True
def check_access_rule(self, operation): """ Add Access rules of mail.message for non-employee user: - read: - raise if the type is comment and subtype NULL (internal note) """ if self.user_has_groups('base.group_public'): self.env.cr.execute( 'SELECT id FROM "%s" WHERE website_published IS NOT TRUE AND id = ANY (%%s)' % (self._table), (self.ids, )) if self.env.cr.fetchall(): raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)' ) % (self._description, operation) + ' - ({} {}, {} {})'.format(_('Records:'), self.ids[:6], _('User:'), self._uid)) return super(MailMessage, self).check_access_rule(operation=operation)
def write(self, vals): """ Synchronize user and its related employee and check access rights if employees are not allowed to update their own data (otherwise sudo is applied for self data). """ hr_fields = { field for field_name, field in self._fields.items() if field.related_field and field.related_field.model_name == 'hr.employee' and field_name in vals } can_edit_self = self.env['ir.config_parameter'].sudo().get_param( 'hr.hr_employee_self_edit') or self.env.user.has_group( 'hr.group_hr_user') if hr_fields and not can_edit_self: # Raise meaningful error message raise AccessError( _("You are only allowed to update your preferences. Please contact a HR officer to update other informations." )) result = super(User, self).write(vals) employee_values = {} for fname in [ f for f in ['name', 'email', 'image_1920', 'tz'] if f in vals ]: employee_values[fname] = vals[fname] if employee_values: if 'email' in employee_values: employee_values['work_email'] = employee_values.pop('email') if 'image_1920' in vals: without_image = self.env['hr.employee'].sudo().search([ ('user_id', 'in', self.ids), ('image_1920', '=', False) ]) with_image = self.env['hr.employee'].sudo().search([ ('user_id', 'in', self.ids), ('image_1920', '!=', False) ]) without_image.write(employee_values) if not can_edit_self: employee_values.pop('image_1920') with_image.write(employee_values) else: self.env['hr.employee'].sudo().search([ ('user_id', 'in', self.ids) ]).write(employee_values) return result
def _compute_kpi_sale_total_value(self): if not self.env.user.has_group( 'sales_team.group_sale_salesman_all_leads'): raise AccessError( _("Do not have access, skip this data for user's digest email") ) for record in self: start, end, company = record._get_kpi_compute_parameters() all_channels_sales = self.env['sale.report'].read_group( [('date', '>=', start), ('date', '<', end), ('state', 'not in', ['draft', 'cancel', 'sent']), ('company_id', '=', company.id)], ['price_total'], ['price_total']) record.kpi_all_sale_total_value = sum([ channel_sale['price_total'] for channel_sale in all_channels_sales ])