This commit is contained in:
李鹏宇
2026-06-30 11:09:43 +08:00
parent a22877068e
commit 5236f01950
2 changed files with 108 additions and 1 deletions
@@ -6,6 +6,7 @@ class ProductTemplate(models.Model):
_inherit = 'product.template'
is_fixed_assets = fields.Boolean(string='是否固定资产', default=True)
journal_item_count = fields.Integer(string='会计凭证行数量', compute='_compute_journal_item_count')
@api.onchange('is_fixed_assets')
def _onchange_fixed_assets(self):
@@ -21,4 +22,99 @@ class ProductTemplate(models.Model):
template_ids = self.env["product.template"].browse(self._context.get('active_ids', self._context.get('active_id')))
company_id = self.env['res.company'].search([('name', '=', '集团本部')])
for template in template_ids:
template.write({'company_id': company_id.id})
template.write({'company_id': company_id.id})
# 常见关联模型映射(表名 → 中文显示名)
_DELETE_CHECK_MODELS = [
# (模型名, 关联字段, 中文名, 描述模板)
('account.move.line', 'product_id', '会计凭证行(Journal Item)', '该产品已被 {count} 条会计凭证行引用'),
('sale.order.line', 'product_template_id', '销售订单行', '该产品已被 {count} 条销售订单行引用'),
('purchase.order.line', 'product_template_id', '采购订单行', '该产品已被 {count} 条采购订单行引用'),
('stock.move', 'product_tmpl_id', '库存移动单', '该产品已被 {count} 条库存移动单引用'),
('stock.move.line', 'product_tmpl_id', '库存移动明细', '该产品已被 {count} 条库存移动明细引用'),
('stock.quant', 'product_tmpl_id', '库存数量记录', '该产品存在 {count} 条库存数量记录'),
('maintenance.equipment', 'product_tmpl_id', '固定资产设备', '该产品已关联 {count} 个设备'),
('mrp.production', 'product_tmpl_id', '生产工单', '该产品已关联 {count} 张生产工单'),
('mrp.bom', 'product_tmpl_id', '物料清单(BOM)', '该产品已关联 {count} 个物料清单'),
('pos.order.line', 'product_id', 'POS订单行', '该产品已被 {count} 条POS订单行引用'),
]
def unlink(self):
"""重写删除方法,删除前检查关联数据并给出友好提示"""
# 逐个检查每个产品的关联情况
blocked_products = []
for record in self:
references = self._check_product_references(record)
if references:
blocked_products.append((record.display_name, references))
# 如果有被阻塞的产品,汇总提示
if blocked_products:
detail_lines = []
for prod_name, refs in blocked_products:
for ref_msg in refs:
detail_lines.append(f'• 【{prod_name}】{ref_msg}')
raise UserError(
'以下产品无法删除,因为它们已被其他业务数据引用:\n\n'
+ '\n'.join(detail_lines) +
'\n\n建议操作:请使用「存档」功能替代「删除」,'
'存档后产品不会在列表中展示且不影响已有数据。'
)
return super().unlink()
def _check_product_references(self, record):
"""检查单个产品的关联引用,返回提示信息列表"""
results = []
# 获取该产品所有变体ID
variant_ids = record.product_variant_ids.ids
for model_name, field_name, cn_name, msg_template in self._DELETE_CHECK_MODELS:
try:
# 检查模型是否存在
if model_name not in self.env:
continue
domain = []
# 根据关联字段是 product_id 还是 product_tmpl_id 构建搜索条件
if field_name == 'product_id':
domain = [('product_id', 'in', variant_ids)]
elif field_name == 'product_template_id' or field_name == 'product_tmpl_id':
domain = [(field_name, '=', record.id)]
count = self.env[model_name].search_count(domain)
if count > 0:
results.append(msg_template.format(count=count))
except Exception:
# 某些模型可能查询异常,跳过不影响主流程
continue
return results
def _compute_journal_item_count(self):
"""计算该产品关联的会计凭证行数量"""
for record in self:
# 获取所有变体ID
variant_ids = record.product_variant_ids.ids
if 'account.move.line' in record.env:
# 按变体product_id统计凭证行数量
count = self.env['account.move.line'].search_count([
('product_id', 'in', variant_ids)
])
record.journal_item_count = count
else:
record.journal_item_count = 0
def action_view_journal_items(self):
"""打开关联的会计凭证行列表"""
self.ensure_one()
variant_ids = self.product_variant_ids.ids
# 构建搜索域:按产品的变体ID查找凭证行
domain = [('product_id', 'in', variant_ids)]
return {
'name': f'{self.display_name} - 会计凭证行',
'type': 'ir.actions.act_window',
'res_model': 'account.move.line',
'view_mode': 'tree,form',
'domain': domain,
'context': {'default_product_id': variant_ids[0] if variant_ids else False},
}
@@ -26,6 +26,17 @@
<label for="is_fixed_assets" string="是否固定资产"/>
<field name="is_fixed_assets" nolabel="1"/>
</xpath>
<!-- 在智能按钮区域添加关联会计凭证行按钮 -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_journal_items"
type="object"
icon="fa-file-text-o"
class="oe_stat_button"
string="会计凭证行">
<field string="会计凭证行" name="journal_item_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
</odoo>