107 lines
4.6 KiB
Python
107 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import logging
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AccountMoveLine(models.Model):
|
|
_inherit = 'account.move.line'
|
|
|
|
used_customer_specific_account = fields.Boolean(
|
|
string='Used Customer Account',
|
|
compute='_compute_account_source',
|
|
store=True,
|
|
help="Indicates if this line used a customer-specific account"
|
|
)
|
|
|
|
account_source = fields.Char(
|
|
string='Account Source',
|
|
compute='_compute_account_source',
|
|
store=True,
|
|
help="Shows whether the account came from Customer or Product Category"
|
|
)
|
|
|
|
@api.depends('move_id', 'move_id.partner_id', 'product_id', 'account_id')
|
|
def _compute_account_source(self):
|
|
"""
|
|
Compute whether this line used a customer-specific account or product category account.
|
|
This provides audit trail information for journal entries.
|
|
"""
|
|
for line in self:
|
|
# Only compute for lines that have an account and are part of a customer invoice
|
|
if not line.account_id or not line.move_id:
|
|
line.used_customer_specific_account = False
|
|
line.account_source = False
|
|
continue
|
|
|
|
# Check if this is a customer invoice (out_invoice or out_refund)
|
|
if line.move_id.move_type not in ('out_invoice', 'out_refund'):
|
|
line.used_customer_specific_account = False
|
|
line.account_source = False
|
|
continue
|
|
|
|
# Get the partner from the move
|
|
partner = line.move_id.partner_id
|
|
if not partner:
|
|
line.used_customer_specific_account = False
|
|
line.account_source = False
|
|
continue
|
|
|
|
# Check if the partner has a customer income account defined
|
|
customer_income_account = partner._get_customer_income_account()
|
|
|
|
# If the line's account matches the customer income account, mark it
|
|
if customer_income_account and line.account_id == customer_income_account:
|
|
line.used_customer_specific_account = True
|
|
line.account_source = 'Customer'
|
|
else:
|
|
line.used_customer_specific_account = False
|
|
line.account_source = 'Product Category'
|
|
|
|
def _compute_account_id(self):
|
|
"""
|
|
Override to inject customer-specific income account logic.
|
|
|
|
This method is called to determine which account to use for invoice lines.
|
|
We call the parent method first, then check if we should override with
|
|
a customer-specific account.
|
|
"""
|
|
# Call parent to get standard account determination
|
|
super()._compute_account_id()
|
|
|
|
# Now override for product lines on customer invoices
|
|
product_lines = self.filtered(lambda line: line.display_type == 'product' and line.move_id.is_invoice(True))
|
|
for line in product_lines:
|
|
if line.product_id and line.move_id.is_sale_document(include_receipts=True):
|
|
partner = line.move_id.partner_id
|
|
if partner:
|
|
try:
|
|
customer_income_account = partner._get_customer_income_account()
|
|
if customer_income_account:
|
|
_logger.debug(
|
|
"Using customer-specific income account %s for partner %s on invoice %s",
|
|
customer_income_account.code, partner.name, line.move_id.name or 'draft'
|
|
)
|
|
|
|
# Apply fiscal position mapping if applicable
|
|
if line.move_id.fiscal_position_id:
|
|
customer_income_account = line.move_id.fiscal_position_id.map_account(customer_income_account)
|
|
|
|
line.account_id = customer_income_account
|
|
except UserError:
|
|
# Re-raise UserError to prevent invoice creation with invalid account
|
|
raise
|
|
except Exception as e:
|
|
# Log unexpected errors and keep the standard account
|
|
_logger.warning(
|
|
"Error determining customer income account for partner %s (ID: %s) on invoice %s: %s. "
|
|
"Using standard product category account.",
|
|
partner.name, partner.id, line.move_id.name or 'draft', str(e)
|
|
)
|
|
|
|
|