customer_cogs_expense_account/models/account_move_line.py
2025-11-25 21:43:35 +07:00

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)
)