oca-financial/odoo-bringout-oca-account-fiscal-rule-account_avatax_oca/account_avatax_oca/models/account_tax.py
2025-08-29 15:43:04 +02:00

131 lines
4.7 KiB
Python

from math import copysign
from odoo import _, api, exceptions, fields, models
from odoo.tools.float_utils import float_compare
class AccountTax(models.Model):
"""Inherit to implement the tax using avatax API"""
_inherit = "account.tax"
is_avatax = fields.Boolean()
@api.model
def _get_avalara_tax_domain(self, tax_rate, doc_type):
return [
("amount", "=", tax_rate),
("is_avatax", "=", True),
(
"company_id",
"=",
self.env.company.id,
),
]
@api.model
def _get_avalara_tax_name(self, tax_rate, doc_type=None):
return _("{}%*").format(str(tax_rate))
@api.model
def get_avalara_tax(self, tax_rate, doc_type):
domain = self._get_avalara_tax_domain(tax_rate, doc_type)
tax = self.with_context(active_test=False).search(domain, limit=1)
if tax and not tax.active:
tax.active = True
if not tax:
domain = self._get_avalara_tax_domain(0, doc_type)
tax_template = self.search(domain, limit=1)
if not tax_template:
raise exceptions.UserError(
_("Please configure Avatax Tax for Company %s:")
% self.env.company.name
)
# If you get a unique constraint error here,
# check the data for your existing Avatax taxes.
vals = {
"amount": tax_rate,
"name": self._get_avalara_tax_name(tax_rate, doc_type),
}
tax = tax_template.sudo().copy(default=vals)
# Odoo core does not use the name set in default dict
tax.name = vals.get("name")
return tax
def compute_all(
self,
price_unit,
currency=None,
quantity=1.0,
product=None,
partner=None,
is_refund=False,
handle_price_include=True,
include_caba_tags=False,
fixed_multiplicator=1,
):
"""
Adopted as the central point to inject custom tax computations.
Avatax logic is triggered if the "avatax_invoice" is set in the context.
To find the Avatax amount, we search an Invoice line with the same
quantity, price and product.
"""
res = super().compute_all(
price_unit,
currency,
quantity,
product,
partner,
is_refund,
handle_price_include,
include_caba_tags,
fixed_multiplicator,
)
avatax_invoice = self.env.context.get("avatax_invoice")
current_aml = False
if "current_aml" in self.env.context:
current_aml = self.env["account.move.line"].browse(
self.env.context.get("current_aml")
)
if not (
current_aml.display_type == "product"
and current_aml.account_type != "asset_receivable"
):
avatax_invoice = False
if avatax_invoice:
# Find the Avatax amount in the invoice Lines
# Looks up the line for the current product, price_unit, and quantity
# Note that the price_unit used must consider discount
base = res["total_excluded"]
digits = 6
avatax_amount = None
if current_aml:
avatax_amount = copysign(current_aml.avatax_amt_line, base)
else:
for line in avatax_invoice.invoice_line_ids:
price_unit = line.currency_id._convert(
price_unit,
avatax_invoice.company_id.currency_id,
avatax_invoice.company_id,
avatax_invoice.date,
)
if (
line.product_id == product
and float_compare(line.quantity, quantity, digits) == 0
):
avatax_amount = copysign(line.avatax_amt_line, base)
break
if avatax_amount is None:
avatax_amount = 0.0
raise exceptions.UserError(
_(
"Incorrect retrieval of Avatax amount for Invoice %(avatax_invoice)s:"
" product %(product.display_name)s, price_unit %(-price_unit)f"
" , quantity %(quantity)f"
)
)
for tax_item in res["taxes"]:
if tax_item["amount"] != 0:
tax_item["amount"] = avatax_amount
res["total_included"] = base + avatax_amount
return res