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

214 lines
7.4 KiB
Python

import logging
import time
from random import random
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from .avatax_rest_api import AvaTaxRESTService
_LOGGER = logging.getLogger(__name__)
class ResPartner(models.Model):
"""
Update partner information by adding new fields
according to Avalara partner configuration
"""
_inherit = "res.partner"
date_validation = fields.Date(
"Last Validation Date",
readonly=True,
copy=False,
help="The date the address was last validated by AvaTax and accepted",
)
validation_method = fields.Selection(
[("avatax", "AVALARA"), ("usps", "USPS"), ("other", "Other")],
"Address Validation Method",
readonly=True,
copy=False,
help="It gets populated when the address is validated by the method",
)
validated_on_save = fields.Boolean(
help="Indicates if the address is already validated on save"
" before calling the wizard",
)
customer_code = fields.Char(copy=False)
tax_exempt = fields.Boolean(
"Is Tax Exempt (Deprecated))",
)
exemption_number = fields.Char(
"Exemption Number (Deprecated)",
)
exemption_code_id = fields.Many2one(
"exemption.code",
"Exemption Code (Deprecated)",
)
property_tax_exempt = fields.Boolean(
"Is Tax Exempt",
company_dependent=True,
help="This company or address can claim for tax exemption",
)
property_exemption_number = fields.Char(
"Exemption Number",
company_dependent=True,
help="The State identification number relevant fot the exemption",
)
property_exemption_code_id = fields.Many2one(
"exemption.code",
"Exemption Code",
company_dependent=True,
help="The type of exemption granted",
)
_sql_constraints = [
("name_uniq", "unique(customer_code)", "Customer Code must be unique!"),
]
@api.depends(
"property_tax_exempt", "property_exemption_code_id", "property_exemption_number"
)
def check_exemption_number(self):
"""
When tax exempt check then atleast exemption number
or exemption code should be filled
"""
for partner in self:
if partner.property_tax_exempt and not (
partner.property_exemption_code_id or partner.property_exemption_number
):
raise UserError(
_(
"Please enter either Exemption Number or Exemption Code"
" for marking customer as Exempt."
)
)
def _get_avatax_customer_code(self):
self.ensure_one()
return "%d-%d-Cust-%d" % (
int(time.time()),
int(random() * 10),
self.id,
)
def generate_cust_code(self):
"Auto populate customer code"
for partner in self:
partner.customer_code = partner._get_avatax_customer_code()
return True
@api.onchange("tax_exempt")
def onchange_tax_exemption(self):
if not self.property_tax_exempt:
self.property_exemption_number = ""
self.property_exemption_code_id = None
def get_state_from_code(self, state_code, country_code):
"""Returns the state from the code."""
state = self.env["res.country.state"].search(
[("code", "=", state_code), ("country_id.code", "=", country_code)],
)
return state
def get_country_from_code(self, code):
"""Returns the country from the code."""
country = self.env["res.country"].search([("code", "=", code)])
return country
def get_valid_address_vals(self, validation_on_save=False):
self.ensure_one()
partner = self
# For automatic validation on save, skip
# if no relevant address details are given
if validation_on_save and not (
partner.city or partner.zip or partner.country_id
):
_LOGGER.info(
"Skipping address validation for %d %s, not enough details.",
partner.id,
partner.display_name,
)
return False
avatax_config = self.env.company.get_avatax_config_company()
# Skip automatic validation for countries not supported by Avatax
supported_countries = [x.code for x in avatax_config.country_ids]
country_code = partner.country_id.code
if validation_on_save and country_code not in supported_countries:
_LOGGER.info(
"Skipping automatic address validation for %d %s"
", country %s not supported.",
partner.id,
partner.display_name,
country_code,
)
return False
avatax_restpoint = AvaTaxRESTService(config=avatax_config)
valid_address = avatax_restpoint.validate_rest_address(
partner.street,
partner.street2,
partner.city,
partner.zip,
partner.state_id.code,
partner.country_id.code,
)
return valid_address
def multi_address_validation(self, validation_on_save=False):
for partner in self:
if not (partner.parent_id and partner.type == "contact"):
valid_address = partner.get_valid_address_vals(
validation_on_save=validation_on_save
)
if valid_address:
partner.write(valid_address)
return True
def button_avatax_validate_address(self):
"""Method is used to verify of state and country"""
view_ref = self.env.ref(
"account_avatax_oca.view_avalara_salestax_address_validate"
)
ctx = self.env.context.copy()
ctx.update({"active_ids": self.ids, "active_id": self.id})
return {
"type": "ir.actions.act_window",
"name": "Address Validation",
"binding_view_types": "form",
"view_mode": "form",
"view_id": view_ref.id,
"res_model": "avalara.salestax.address.validate",
"nodestroy": True,
"res_id": False,
"target": "new",
"context": ctx,
}
@api.model_create_multi
def create(self, vals_list):
partners = super().create(vals_list)
avatax_config = self.env.company.get_avatax_config_company()
for partner in partners:
# Auto populate customer code, if not provided
if not partner.customer_code:
partner.generate_cust_code()
# Auto validate address, if enabled
if avatax_config.validation_on_save:
partner.multi_address_validation(validation_on_save=True)
partner.validated_on_save = True
return partners
def write(self, vals):
res = super(ResPartner, self).write(vals)
address_fields = ["street", "street2", "city", "zip", "state_id", "country_id"]
if not self.env.context.get("avatax_writing") and any(
x in vals for x in address_fields
):
partner = self.with_context(avatax_writing=True)
avatax_config = self.env.company.get_avatax_config_company()
if avatax_config.validation_on_save:
partner.multi_address_validation(validation_on_save=True)
partner.validated_on_save = True
return res