This commit is contained in:
jinling.yang
2024-06-28 10:10:46 +08:00
3 changed files with 110 additions and 140 deletions

View File

@@ -200,7 +200,7 @@ def _create(self, data_list):
@api.model @api.model
def unlink(self, ids): def unlink(self):
""" unlink() """ unlink()
Deletes the records in ``self``. Deletes the records in ``self``.
@@ -210,148 +210,118 @@ def unlink(self, ids):
""" """
if not self: if not self:
return True return True
if not ids:
return True
self.check_access_rights('unlink') self.check_access_rights('unlink')
self.check_access_rule('unlink') self.check_access_rule('unlink')
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
for record_id in ids: for func in self._ondelete_methods:
record = self.browse(record_id) # func._ondelete is True if it should be called during uninstallation
for func in record._ondelete_methods: if func._ondelete or not self._context.get(MODULE_UNINSTALL_FLAG):
# func._ondelete is True if it should be called during uninstallation func(self)
if func._ondelete or not record._context.get(MODULE_UNINSTALL_FLAG):
func(record)
# TOFIX: this avoids an infinite loop when trying to recompute a # TOFIX: this avoids an infinite loop when trying to recompute a
# field, which triggers the recomputation of another field using the # field, which triggers the recomputation of another field using the
# same compute function, which then triggers again the computation # same compute function, which then triggers again the computation
# of those two fields # of those two fields
for field in record._fields.values(): for field in self._fields.values():
record.env.remove_to_compute(field, record) self.env.remove_to_compute(field, self)
record.env.flush_all() self.env.flush_all()
cr = record._cr cr = self._cr
Data = record.env['ir.model.data'].sudo().with_context({}) Data = self.env['ir.model.data'].sudo().with_context({})
Defaults = record.env['ir.default'].sudo() Defaults = self.env['ir.default'].sudo()
Property = record.env['ir.property'].sudo() Property = self.env['ir.property'].sudo()
Attachment = record.env['ir.attachment'].sudo() Attachment = self.env['ir.attachment'].sudo()
ir_property_unlink = Property ir_property_unlink = Property
ir_model_data_unlink = Data ir_model_data_unlink = Data
ir_attachment_unlink = Attachment ir_attachment_unlink = Attachment
# mark fields that depend on 'record' to recompute them after 'record' has # mark fields that depend on 'self' to recompute them after 'self' has
# been deleted (like updating a sum of lines after deleting one line) # been deleted (like updating a sum of lines after deleting one line)
with record.env.protecting(record._fields.values(), record): with self.env.protecting(self._fields.values(), self):
record.modified(record._fields, before=True) self.modified(self._fields, before=True)
for sub_ids in cr.split_for_in_conditions(record.ids): for sub_ids in cr.split_for_in_conditions(self.ids):
records = record.browse(sub_ids) records = self.browse(sub_ids)
# Check if the records are used as default properties. # Check if the records are used as default properties.
refs = [f'{record._name},{id_}' for id_ in sub_ids] refs = [f'{self._name},{id_}' for id_ in sub_ids]
if Property.search( if Property.search(
[('res_id', '=', False), ('value_reference', 'in', refs)], [('res_id', '=', False), ('value_reference', 'in', refs)],
limit=1): limit=1):
raise UserError( raise UserError(
_('Unable to delete this document because it is used as a default property')) _('Unable to delete this document because it is used as a default property'))
# Delete the records' properties. # Delete the records' properties.
ir_property_unlink |= Property.search([('res_id', 'in', refs)]) ir_property_unlink |= Property.search([('res_id', 'in', refs)])
query = f'DELETE FROM "{record._table}" WHERE id IN %s' query = f'DELETE FROM "{self._table}" WHERE id IN %s'
cr.execute(query, (sub_ids,)) cr.execute(query, (sub_ids,))
# Removing the ir_model_data reference if the record being deleted # Removing the ir_model_data reference if the record being deleted
# is a record created by xml/csv file, as these are not connected # is a record created by xml/csv file, as these are not connected
# with real database foreign keys, and would be dangling references. # with real database foreign keys, and would be dangling references.
# #
# Note: the following steps are performed as superuser to avoid # Note: the following steps are performed as superuser to avoid
# access rights restrictions, and with no context to avoid possible # access rights restrictions, and with no context to avoid possible
# side-effects during admin calls. # side-effects during admin calls.
data = Data.search( data = Data.search(
[('model', '=', record._name), ('res_id', 'in', sub_ids)]) [('model', '=', self._name), ('res_id', 'in', sub_ids)])
ir_model_data_unlink |= data ir_model_data_unlink |= data
# For the same reason, remove the defaults having some of the # For the same reason, remove the defaults having some of the
# records as value # records as value
Defaults.discard_records(records) Defaults.discard_records(records)
# For the same reason, remove the relevant records in ir_attachment # For the same reason, remove the relevant records in ir_attachment
# (the search is performed with sql as the search method of # (the search is performed with sql as the search method of
# ir_attachment is overridden to hide attachments of deleted # ir_attachment is overridden to hide attachments of deleted
# records) # records)
query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s' query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s'
cr.execute(query, (record._name, sub_ids)) cr.execute(query, (self._name, sub_ids))
ir_attachment_unlink |= Attachment.browse( ir_attachment_unlink |= Attachment.browse(
row[0] for row in cr.fetchall()) row[0] for row in cr.fetchall())
# invalidate the *whole* cache, since the orm does not handle all # invalidate the *whole* cache, since the orm does not handle all
# changes made in the database, like cascading delete! # changes made in the database, like cascading delete!
record.env.invalidate_all(flush=False) self.env.invalidate_all(flush=False)
if ir_property_unlink: if ir_property_unlink:
ir_property_unlink.unlink() ir_property_unlink.unlink()
if ir_model_data_unlink: if ir_model_data_unlink:
ir_model_data_unlink.unlink() ir_model_data_unlink.unlink()
if ir_attachment_unlink: if ir_attachment_unlink:
ir_attachment_unlink.unlink() ir_attachment_unlink.unlink()
# DLE P93: flush after the unlink, for recompute fields depending on # DLE P93: flush after the unlink, for recompute fields depending on
# the modified of the unlink # the modified of the unlink
record.env.flush_all() self.env.flush_all()
# auditing: deletions are infrequent and leave no trace in the database # auditing: deletions are infrequent and leave no trace in the database
_unlink.info('User #%s deleted %s records with IDs: %r', record._uid, _unlink.info('User #%s deleted %s records with IDs: %r', self._uid,
record._name, record.ids) self._name, self.ids)
# This is used to restrict the access right to unlink a record # This is used to restrict the access right to unlink a record
current_model_id = record.env['ir.model'].sudo().search( current_model_id = self.env['ir.model'].sudo().search(
[('model', '=', record._name)]).id [('model', '=', self._name)]).id
# access_right_rec = record.env['access.right'].sudo().search_read( access_right_rec = self.env['access.right'].sudo().search_read(
# [('model_id', '=', current_model_id)], ['model_id', 'is_delete', [('model_id', '=', current_model_id)], ['model_id', 'is_delete',
# 'groups_id']) 'groups_id'])
# if access_right_rec and not record.env.is_admin(): if access_right_rec and not self.env.is_admin():
# for rec in access_right_rec: for rec in access_right_rec:
# group_name = record.env['ir.model.data'].sudo().search([ group_name = self.env['ir.model.data'].sudo().search([
# ('model', '=', 'res.groups'), ('model', '=', 'res.groups'),
# ('res_id', '=', rec['groups_id'][0]) ('res_id', '=', rec['groups_id'][0])
# ]).name ]).name
# module_name = record.env['ir.model.data'].sudo().search([ module_name = self.env['ir.model.data'].sudo().search([
# ('model', '=', 'res.groups'), ('model', '=', 'res.groups'),
# ('res_id', '=', rec['groups_id'][0]) ('res_id', '=', rec['groups_id'][0])
# ]).module ]).module
# group = module_name + "." + group_name group = module_name + "." + group_name
# if record.env.user.has_group(group): if self.env.user.has_group(group):
# if rec['is_delete']: if rec['is_delete']:
# raise UserError(_('You are restricted from performing this' raise UserError(_('You are restricted from performing this'
# ' operation. Please contact the' ' operation. Please contact the'
# ' administrator.')) ' administrator.'))
# 检查 'access.right' 模型是否存在于环境中 return True
if 'access.right' in record.env:
# current_model_id = record.env['ir.model'].sudo().search([('model', '=', record._name)]).id
access_right_rec = record.env['access.right'].sudo().search_read(
[('model_id', '=', current_model_id)], ['model_id', 'is_delete', 'groups_id']
)
if access_right_rec and not record.env.is_admin():
for rec in access_right_rec:
group_data = record.env['ir.model.data'].sudo().search_read(
[('model', '=', 'res.groups'), ('res_id', '=', rec['groups_id'][0])],
['name', 'module']
)
if group_data:
group_name = group_data[0]['name']
module_name = group_data[0]['module']
group_xml_id = f"{module_name}.{group_name}"
if record.env.user.has_group(group_xml_id) and rec['is_delete']:
raise UserError(
_('You are restricted from performing this operation. Please contact the administrator.'))
else:
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
pass
return True
BaseModel._create = _create BaseModel._create = _create
BaseModel.unlink = unlink BaseModel.unlink = unlink

View File

@@ -37,7 +37,7 @@ class sf_production_plan(models.Model):
_order = 'state_order asc, write_date desc' _order = 'state_order asc, write_date desc'
name = fields.Char(string='制造订单') name = fields.Char(string='制造订单')
active = fields.Boolean(string='已归档', default=True) # active = fields.Boolean(string='已归档', default=True)
# selected = fields.Boolean(default=False) # selected = fields.Boolean(default=False)
# order_number = fields.Char(string='订单号') # order_number = fields.Char(string='订单号')
order_deadline = fields.Datetime(string='订单交期') order_deadline = fields.Datetime(string='订单交期')
@@ -101,17 +101,17 @@ class sf_production_plan(models.Model):
# return super(sf_production_plan, self.with_context(active_test=False))._search( # return super(sf_production_plan, self.with_context(active_test=False))._search(
# args, offset, limit, order, count, access_rights_uid) # args, offset, limit, order, count, access_rights_uid)
def archive(self): # def archive(self):
""" # """
归档 # 归档
""" # """
self.write({'active': False}) # self.write({'active': False})
#
def unarchive(self): # def unarchive(self):
""" # """
取消归档 # 取消归档
""" # """
self.write({'active': True}) # self.write({'active': True})
@api.model @api.model
def get_import_templates(self): def get_import_templates(self):

View File

@@ -63,7 +63,7 @@
</div> </div>
<group> <group>
<group string="基本信息"> <group string="基本信息">
<field name="active" invisible="1"/> <!-- <field name="active" invisible="1"/> -->
<field name="production_id" widget="many2one_button"/> <field name="production_id" widget="many2one_button"/>
<field name="product_id"/> <field name="product_id"/>
<field name="origin"/> <field name="origin"/>