You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Odoo 17中如何显示hr.applicant的归档重复记录?

问题描述

开发的重复记录检测功能可在弹窗下拉列表展示活跃重复项,但无法显示归档状态的重复记录。日志显示已检测到归档重复项(ID:8,active为False),但下拉列表中无法看到该记录。

hr_applicant模型代码

selected_duplicate_id = fields.Many2one('hr.applicant', string="选中的重复项", domain="[('id', 'in', duplicate_ids)]")

duplicate_ids = fields.Many2many(
    'hr.applicant',
    string="重复项",
    compute='_compute_duplicates',
    store=False
)
show_duplicates_warning = fields.Boolean(
    string="显示警告",
    compute='_compute_duplicates'
)

@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
    args = list(args or [])
    _logger.info("name_search called with name=%s, args=%s, context=%s", name, args, self._context)

    if self._context.get('active_test') is False:
        args = [arg for arg in args if arg[0] != 'active']
        args = expression.AND([args, [('active', 'in', [True, False])]])

    duplicate_ids = self._context.get('duplicate_ids', [])
    if duplicate_ids:
        args = expression.AND([args, [('id', 'in', duplicate_ids)]])
    else:
        for arg in args:
            if arg[0] == 'id' and arg[1] == 'in' and not arg[2]:
                _logger.warning("Empty id list in name_search args: %s", args)
                return []

    result = super(Applicant, self).name_search(name=name, args=args, operator=operator, limit=limit)
    _logger.info("name_search result: %s", result)
    return result

@api.depends('email_from', 'partner_mobile_sanitized')
def _compute_duplicates(self):
    for applicant in self:
        duplicates = self.env['hr.applicant']
        if applicant.email_normalized or applicant.partner_mobile_sanitized:
            domain = applicant._get_similar_applicants_domain()
            _logger.info("Domain for applicant %s: %s", applicant.id, domain)
            if domain:
                duplicates = self.env['hr.applicant'].with_context(active_test=False).search(domain)
            _logger.info("Duplicates for applicant %s: %s (Active: %s)",
                         applicant.id, duplicates.ids, [d.active for d in duplicates])
        applicant.duplicate_ids = duplicates
        applicant.show_duplicates_warning = bool(duplicates)
        if applicant.selected_duplicate_id and applicant.selected_duplicate_id.id not in duplicates.ids:
            applicant.selected_duplicate_id = False

@api.depends('email_from', 'partner_mobile_sanitized')
def _compute_application_count(self):
    if not any(self._ids):
        for applicant in self:
            duplicates = self.env['hr.applicant'].with_context(
                active_test=False
            )
            if applicant.email_normalized or applicant.partner_mobile_sanitized:
                domain = applicant._get_similar_applicants_domain()
                if domain:
                    duplicates = self.env['hr.applicant'].with_context(
                        active_test=False
                    ).search(domain)
            applicant.application_count = len(duplicates)
            _logger.info("Found duplicates (active/inactive): %s",
                         [(d.id, d.partner_name, d.active) for d in applicant.duplicate_ids])
        return

    self.flush_recordset(['email_normalized', 'partner_mobile_sanitized'])
    self.env.cr.execute("""
            SELECT
                a.id,
                (
                    SELECT COUNT(*)
                    FROM hr_applicant AS sub
                    WHERE a.id != sub.id
                    AND sub.create_date < a.create_date
                    AND ((a.email_normalized <> '' AND a.email_normalized = sub.email_normalized)
                        OR (a.partner_mobile_sanitized <> '' AND a.partner_mobile_sanitized = sub.partner_mobile_sanitized))
                    AND (sub.active = TRUE OR sub.active = FALSE)
                ) AS similar_applicants
            FROM hr_applicant AS a
            WHERE id IN %(ids)s
        """, {'ids': tuple(self._origin.ids)})
    query_results = self.env.cr.dictfetchall()
    mapped_data = {result['id']: result['similar_applicants'] for result in query_results}
    for applicant in self:
        applicant.application_count = mapped_data.get(applicant.id, 0)

def action_show_duplicates_dialog(self):
    self.ensure_one()
    self._compute_duplicates()
    _logger.info("Opening duplicates dialog for applicant %s with duplicates: %s",
                 self.id, self.duplicate_ids.ids)
    return {
        'name': '检测到重复项',
        'type': 'ir.actions.act_window',
        'res_model': 'hr.applicant',
        'view_mode': 'form',
        'views': [(self.env.ref('hr_recruitment.hr_applicant_view_form').id, 'form')],
        'target': 'new',
        'res_id': self.id,
        'context': {
            'dialog_size': 'large',
            'create': False,
            'edit': False,
            'active_test': False,
            'default_duplicate_ids': self.duplicate_ids.ids,
            'duplicate_ids': self.duplicate_ids.ids,
        }
    }

XML表单代码

<h4><i class="fa fa-warning me-2"/>检测到重复项!</h4>
<field name="duplicate_ids" widget="many2many_tags" options="{'no_create': True}"/>

<div class="row mt-2">
    <div class="col-md-12">
        <div class="o_form_label">选择要合并的主记录:</div>
        <field name="selected_duplicate_id"
               widget="many2one"
               options="{'no_create': True, 'no_open': True}"
               context="{'active_test': False, 'hr_force_show_archived': True, 'search_default_duplicates': True}"
               domain="[('id', 'in', duplicate_ids)]"
               class="oe_inline w-100"/>
    </div>
</div>

日志信息

odoo-1  | 2025-07-18 15:13:31,399 1 INFO Test odoo.tools.translate: Domain for applicant 9: ['&', '&', '|', ('email_normalized', '=ilike', 'test@gmail.com'), ('partner_mobile_sanitized', '=', '958604986'), ('id', '!=', 9), ('active', 'in', [True, False])] 
odoo-1  | 2025-07-18 15:13:31,401 1 INFO Test odoo.tools.translate: Duplicates for applicant 9: [8] (Active: [False]) 

排查与解决步骤

从日志可知,_compute_duplicates已正确获取到归档重复记录,但下拉列表不显示,问题出在name_search逻辑或上下文传递:

  1. 修正name_search的active过滤逻辑
    当前代码用expression.AND合并条件可能存在优先级问题,直接追加active条件更可靠:

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        args = list(args or [])
        _logger.info("name_search called with name=%s, args=%s, context=%s", name, args, self._context)
    
        if self._context.get('active_test') is False:
            # 移除所有active相关条件,明确包含所有状态记录
            args = [arg for arg in args if arg[0] != 'active']
            args.append(('active', 'in', [True, False]))
    
        duplicate_ids = self._context.get('duplicate_ids', [])
        if duplicate_ids:
            args = expression.AND([args, [('id', 'in', duplicate_ids)]])
        else:
            for arg in args:
                if arg[0] == 'id' and arg[1] == 'in' and not arg[2]:
                    _logger.warning("Empty id list in name_search args: %s", args)
                    return []
    
        result = super(Applicant, self).name_search(name=name, args=args, operator=operator, limit=limit)
        _logger.info("name_search result: %s", result)
        return result
    
  2. 确认上下文传递有效性
    检查name_search日志中的self._context是否包含active_test=False和duplicate_ids=[8]。如果上下文未正确传递,需确保弹窗的context与字段的context叠加生效(当前XML和弹窗逻辑已设置,大概率没问题)。

  3. 验证返回结果
    修改后查看name_search result日志,确认返回结果包含ID8的记录。若后端返回但前端不显示,需检查前端是否有额外过滤逻辑(Odoo默认不会做此处理)。


内容的提问来源于stack exchange,提问作者tonbari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 15:44:54