189 lines
7.3 KiB
Python
189 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2026 Nexa Systems Inc.
|
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
|
|
|
import re
|
|
import logging
|
|
|
|
from odoo import api, fields, models, _
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FusionFaxRC(models.Model):
|
|
"""Extend fusion.fax with contact matching, smart buttons,
|
|
forward fax, and send-new-fax actions."""
|
|
|
|
_inherit = 'fusion.fax'
|
|
|
|
invoice_count = fields.Integer(
|
|
string='Invoices', compute='_compute_partner_counts',
|
|
)
|
|
sale_order_count = fields.Integer(
|
|
string='Sales Orders', compute='_compute_partner_counts',
|
|
)
|
|
|
|
@api.depends('partner_id')
|
|
def _compute_partner_counts(self):
|
|
for rec in self:
|
|
if rec.partner_id:
|
|
partner = rec.partner_id.commercial_partner_id or rec.partner_id
|
|
partner_ids = (partner | partner.child_ids).ids
|
|
rec.sale_order_count = self.env['sale.order'].search_count(
|
|
[('partner_id', 'in', partner_ids)],
|
|
)
|
|
rec.invoice_count = self.env['account.move'].search_count(
|
|
[('partner_id', 'in', partner_ids),
|
|
('move_type', 'in', ('out_invoice', 'out_refund'))],
|
|
)
|
|
else:
|
|
rec.sale_order_count = 0
|
|
rec.invoice_count = 0
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
for vals in vals_list:
|
|
if not vals.get('partner_id'):
|
|
partner = self._match_fax_partner(
|
|
vals.get('fax_number', ''),
|
|
vals.get('sender_number', ''),
|
|
vals.get('direction', ''),
|
|
)
|
|
if partner:
|
|
vals['partner_id'] = partner.id
|
|
return super().create(vals_list)
|
|
|
|
@api.model
|
|
def _match_fax_partner(self, fax_number, sender_number, direction):
|
|
"""Match a fax to a contact by fax number, phone, or mobile."""
|
|
search_number = sender_number if direction == 'inbound' else fax_number
|
|
if not search_number:
|
|
return False
|
|
|
|
cleaned = self._normalize_fax_phone(search_number)
|
|
if not cleaned or len(cleaned) < 7:
|
|
return False
|
|
|
|
Partner = self.env['res.partner']
|
|
|
|
if 'x_ff_fax_number' in Partner._fields:
|
|
partners = Partner.search(
|
|
[('x_ff_fax_number', '!=', False)],
|
|
order='customer_rank desc, write_date desc',
|
|
)
|
|
for p in partners:
|
|
if self._normalize_fax_phone(p.x_ff_fax_number) == cleaned:
|
|
return p
|
|
|
|
phone_fields = [f for f in ('phone', 'mobile') if f in Partner._fields]
|
|
if phone_fields:
|
|
domain = ['|'] * (len(phone_fields) - 1)
|
|
for f in phone_fields:
|
|
domain.append((f, '!=', False))
|
|
partners = Partner.search(domain, order='customer_rank desc, write_date desc')
|
|
for p in partners:
|
|
for f in phone_fields:
|
|
val = p[f]
|
|
if val and self._normalize_fax_phone(val) == cleaned:
|
|
return p
|
|
|
|
return False
|
|
|
|
@staticmethod
|
|
def _normalize_fax_phone(number):
|
|
return re.sub(r'\D', '', number or '')[-10:] if number else ''
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Smart button actions
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
def action_view_contact(self):
|
|
self.ensure_one()
|
|
if not self.partner_id:
|
|
return
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'res.partner',
|
|
'res_id': self.partner_id.id,
|
|
'view_mode': 'form',
|
|
'target': 'current',
|
|
}
|
|
|
|
def action_view_sale_orders(self):
|
|
self.ensure_one()
|
|
if not self.partner_id:
|
|
return
|
|
partner = self.partner_id.commercial_partner_id or self.partner_id
|
|
partner_ids = (partner | partner.child_ids).ids
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Sales Orders - %s') % self.partner_id.name,
|
|
'res_model': 'sale.order',
|
|
'view_mode': 'list,form',
|
|
'domain': [('partner_id', 'in', partner_ids)],
|
|
'target': 'current',
|
|
}
|
|
|
|
def action_view_invoices(self):
|
|
self.ensure_one()
|
|
if not self.partner_id:
|
|
return
|
|
partner = self.partner_id.commercial_partner_id or self.partner_id
|
|
partner_ids = (partner | partner.child_ids).ids
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Invoices - %s') % self.partner_id.name,
|
|
'res_model': 'account.move',
|
|
'view_mode': 'list,form',
|
|
'domain': [('partner_id', 'in', partner_ids),
|
|
('move_type', 'in', ('out_invoice', 'out_refund'))],
|
|
'target': 'current',
|
|
}
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Forward Fax (received fax -> send to someone else)
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
def action_forward_fax(self):
|
|
"""Open Send Fax wizard with this fax's documents pre-attached."""
|
|
self.ensure_one()
|
|
attachment_ids = self.document_ids.sorted('sequence').mapped('attachment_id').ids
|
|
ctx = {
|
|
'default_document_source_fax_id': self.id,
|
|
'forward_attachment_ids': attachment_ids,
|
|
}
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Forward Fax - %s') % self.name,
|
|
'res_model': 'fusion_faxes.send.fax.wizard',
|
|
'view_mode': 'form',
|
|
'target': 'new',
|
|
'context': ctx,
|
|
}
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Send New Fax (pre-fill contact from current fax)
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
def action_send_new_fax(self):
|
|
"""Open Send Fax wizard pre-filled with this fax's contact info."""
|
|
self.ensure_one()
|
|
ctx = {}
|
|
if self.partner_id:
|
|
ctx['default_partner_id'] = self.partner_id.id
|
|
fax_num = ''
|
|
if self.direction == 'inbound':
|
|
fax_num = self.sender_number or self.fax_number
|
|
else:
|
|
fax_num = self.fax_number
|
|
if fax_num:
|
|
ctx['default_fax_number'] = fax_num
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Send New Fax'),
|
|
'res_model': 'fusion_faxes.send.fax.wizard',
|
|
'view_mode': 'form',
|
|
'target': 'new',
|
|
'context': ctx,
|
|
}
|