mirror of
https://github.com/bringout/oca-financial.git
synced 2026-04-27 12:22:01 +02:00
328 lines
11 KiB
Python
328 lines
11 KiB
Python
import logging
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
|
|
from .avatax_rest_api import AvaTaxRESTService
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ExemptionCode(models.Model):
|
|
_name = "exemption.code"
|
|
_description = "Exemption Code"
|
|
|
|
name = fields.Char(required=True)
|
|
code = fields.Char()
|
|
|
|
@api.depends("name", "code")
|
|
def name_get(self):
|
|
def name(r):
|
|
return r.code and "({}) {}".format(r.code, r.name) or r.name
|
|
|
|
return [(r.id, name(r)) for r in self]
|
|
|
|
|
|
class AvalaraSalestax(models.Model):
|
|
_name = "avalara.salestax"
|
|
_description = "AvaTax Configuration"
|
|
_rec_name = "account_number"
|
|
|
|
@api.model
|
|
def _get_avatax_supported_countries(self):
|
|
"""Returns the countries supported by AvaTax Address Validation Service."""
|
|
return self.env["res.country"].search([("code", "in", ["US", "CA"])])
|
|
|
|
account_number = fields.Char(
|
|
name="Account ID", required=True, help="Account Number provided by AvaTax"
|
|
)
|
|
license_key = fields.Char(required=True, help="License Key provided by AvaTax")
|
|
service_url = fields.Selection(
|
|
[
|
|
("https://rest.avatax.com/api/v2", "Production (REST API)"),
|
|
("https://sandbox-rest.avatax.com/api/v2", "Sandbox (REST API)"),
|
|
],
|
|
string="Service URL",
|
|
default="https://rest.avatax.com/api/v2",
|
|
help="The url to connect with",
|
|
)
|
|
request_timeout = fields.Integer(
|
|
default=300,
|
|
help="Defines AvaTax request time out length"
|
|
", AvaTax best practices prescribes default setting of 300 seconds",
|
|
)
|
|
company_code = fields.Char(
|
|
default="DEFAULT",
|
|
required=True,
|
|
help="The company code as defined in the Admin Console of AvaTax",
|
|
)
|
|
logging = fields.Boolean(
|
|
"Log API Requests",
|
|
help="Enables detailed AvaTax transaction logging within application",
|
|
)
|
|
result_in_uppercase = fields.Boolean(
|
|
"Return validation results in upper case",
|
|
help="The address validation results are returned in in upper case",
|
|
)
|
|
disable_address_validation = fields.Boolean(
|
|
help="Disables the ability to perform address validation"
|
|
)
|
|
validation_on_save = fields.Boolean(
|
|
"Automatic Address Validation",
|
|
help="Automatically validates addresses when they are created or modified",
|
|
)
|
|
force_address_validation = fields.Boolean(
|
|
"Require Validated Addresses",
|
|
help="Only compute taxes if addresses were validated by the Avatax service",
|
|
)
|
|
auto_generate_customer_code = fields.Boolean(
|
|
"Automatically generate missing customer code",
|
|
default=True,
|
|
help="This will generate customer code for the customer used in the "
|
|
"transaction, if it doesn't have one already. "
|
|
"Each code is unique per customer. "
|
|
"When this is disabled, you will have to manually go to each customer "
|
|
"and manually generate their customer code. "
|
|
"This is required for Avatax and is only generated one time.",
|
|
)
|
|
disable_tax_calculation = fields.Boolean(
|
|
"Disable AvaTax Calculation",
|
|
help="No tax calculation requests will be sent to the AvaTax web service.",
|
|
)
|
|
# TODO: Control - Disable Document Recording
|
|
# In order for this connector to be used in conjunction
|
|
# with other integrations to AvaTax, the user must be able to control which connector
|
|
# is used for recording documents to AvaTax.
|
|
# From a technical standpoint, simply use DocType: 'SalesOrder' on all calls
|
|
# and suppress any non-getTax calls (i.e. cancelTax, postTax).
|
|
disable_tax_reporting = fields.Boolean(
|
|
"Disable Document Recording/Commiting",
|
|
help="No transactions will be recorded (commited) to the Avatax service.",
|
|
)
|
|
country_ids = fields.Many2many(
|
|
"res.country",
|
|
"avalara_salestax_country_rel",
|
|
"avalara_salestax_id",
|
|
"country_id",
|
|
"Countries",
|
|
default=_get_avatax_supported_countries,
|
|
help="Countries where address validation will be used",
|
|
)
|
|
active = fields.Boolean(
|
|
default=True,
|
|
help="Uncheck the active field to hide the record",
|
|
)
|
|
company_id = fields.Many2one(
|
|
"res.company",
|
|
"Company",
|
|
required=True,
|
|
default=lambda self: self.env.company,
|
|
help="Company which has subscribed to the AvaTax service",
|
|
)
|
|
company_partner_id = fields.Many2one(
|
|
string="Company Address",
|
|
related="company_id.partner_id",
|
|
)
|
|
upc_enable = fields.Boolean(
|
|
"Enable UPC Taxability",
|
|
help="Allows ean13 to be reported in place of Item Reference"
|
|
" as upc identifier.",
|
|
)
|
|
invoice_calculate_tax = fields.Boolean(
|
|
"Auto Calculate Tax on Invoice Save",
|
|
help="Automatically triggers API to calculate tax If changes made on"
|
|
"Invoice's warehouse_id, tax_on_shipping_address, "
|
|
"Invoice line's price_unit, discount, quantity",
|
|
)
|
|
use_so_partner_id = fields.Boolean(
|
|
string="Use Sale Customer Code on Invoice",
|
|
help="Use Sales Order's Customer field to determine Taxable "
|
|
"Status on the Customer Invoice. If no Sales Order exists, "
|
|
"Customer field on the invoice form view will be used instead",
|
|
)
|
|
hide_exemption = fields.Boolean(
|
|
"Hide Exemption & Tax Based on shipping address",
|
|
default=False,
|
|
help="Uncheck the this field to show exemption fields on SO/Invoice form view. "
|
|
"Also, it will show Tax based on shipping address button",
|
|
)
|
|
# TODO: add option to Display Prices with Tax Included
|
|
# Enabled the tax inclusive flag in the GetTax Request.
|
|
|
|
# constraints on uniq records creation with account_number and company_id
|
|
_sql_constraints = [
|
|
(
|
|
"code_company_uniq",
|
|
"unique (company_code)",
|
|
"Avalara setting is already available for this company code",
|
|
),
|
|
(
|
|
"account_number_company_uniq",
|
|
"unique (account_number, company_id)",
|
|
"The account number must be unique per company!",
|
|
),
|
|
]
|
|
|
|
def get_avatax_rest_service(self):
|
|
self.ensure_one()
|
|
if self.disable_tax_calculation:
|
|
_logger.info(
|
|
"Avatax tax calculation is disabled, skipping Avatax API contact."
|
|
)
|
|
return False
|
|
return AvaTaxRESTService(
|
|
self.account_number,
|
|
self.license_key,
|
|
self.service_url,
|
|
self.request_timeout,
|
|
self.logging,
|
|
config=self,
|
|
)
|
|
|
|
def create_transaction(
|
|
self,
|
|
doc_date,
|
|
doc_code,
|
|
doc_type,
|
|
partner,
|
|
ship_from_address,
|
|
shipping_address,
|
|
lines,
|
|
user=None,
|
|
exemption_number=None,
|
|
exemption_code_name=None,
|
|
commit=False,
|
|
invoice_date=None,
|
|
reference_code=None,
|
|
location_code=None,
|
|
is_override=None,
|
|
currency_id=None,
|
|
ignore_error=None,
|
|
log_to_record=False,
|
|
):
|
|
self.ensure_one()
|
|
avatax_config = self
|
|
|
|
currency_code = self.env.company.currency_id.name
|
|
if currency_id:
|
|
currency_code = currency_id.name
|
|
|
|
if not partner.customer_code:
|
|
if not avatax_config.auto_generate_customer_code:
|
|
raise UserError(
|
|
_(
|
|
"Customer Code for customer %(partner.name)s not defined.\n\n "
|
|
"You can edit the Customer Code in customer profile. "
|
|
'You can fix by clicking "Generate Customer Code" '
|
|
"button in the customer contact information"
|
|
)
|
|
)
|
|
else:
|
|
partner.generate_cust_code()
|
|
|
|
if not shipping_address:
|
|
raise UserError(
|
|
_("There is no source shipping address defined for partner %s.")
|
|
% partner.name
|
|
)
|
|
|
|
if not ship_from_address:
|
|
raise UserError(_("There is no Company address defined."))
|
|
|
|
if avatax_config.validation_on_save:
|
|
for address in [partner, shipping_address, ship_from_address]:
|
|
if not address.date_validation:
|
|
address.multi_address_validation(validation_on_save=True)
|
|
|
|
# this condition is required, in case user select force address validation
|
|
# on AvaTax API Configuration
|
|
if (
|
|
avatax_config.force_address_validation
|
|
and not avatax_config.disable_address_validation
|
|
):
|
|
if not shipping_address.date_validation:
|
|
raise UserError(
|
|
_(
|
|
"Please validate the shipping address for the partner %(partner.name)s."
|
|
)
|
|
)
|
|
|
|
# if not avatax_config.address_validation:
|
|
if not ship_from_address.date_validation:
|
|
raise UserError(_("Please validate the origin warehouse address."))
|
|
|
|
if avatax_config.disable_tax_calculation:
|
|
_logger.info(
|
|
"Avatax tax calculation is disabled. Skipping %s %s.",
|
|
doc_code,
|
|
doc_type,
|
|
)
|
|
return False
|
|
|
|
if commit and avatax_config.disable_tax_reporting:
|
|
_logger.warning(
|
|
_("Avatax commiting document %s, but it tax reporting is disabled."),
|
|
doc_code,
|
|
)
|
|
|
|
avatax = self.get_avatax_rest_service()
|
|
result = avatax.get_tax(
|
|
avatax_config.company_code,
|
|
doc_date,
|
|
doc_type,
|
|
partner.customer_code,
|
|
doc_code,
|
|
ship_from_address,
|
|
shipping_address,
|
|
lines,
|
|
exemption_number,
|
|
exemption_code_name,
|
|
user and user.name or None,
|
|
commit and not avatax_config.disable_tax_reporting,
|
|
invoice_date,
|
|
reference_code,
|
|
location_code,
|
|
currency_code,
|
|
partner.vat or None,
|
|
is_override,
|
|
ignore_error=ignore_error,
|
|
log_to_record=log_to_record,
|
|
)
|
|
return result
|
|
|
|
def commit_transaction(self, doc_code, doc_type):
|
|
self.ensure_one()
|
|
result = False
|
|
if not self.disable_tax_reporting:
|
|
avatax = self.get_avatax_rest_service()
|
|
result = avatax.call(
|
|
"commit_transaction", self.company_code, doc_code, {"commit": True}
|
|
)
|
|
return result
|
|
|
|
def void_transaction(self, doc_code, doc_type):
|
|
if self:
|
|
self.ensure_one()
|
|
result = False
|
|
if not self.disable_tax_reporting:
|
|
avatax = self.get_avatax_rest_service()
|
|
result = avatax.call(
|
|
"void_transaction",
|
|
self.company_code,
|
|
doc_code,
|
|
{"code": "DocVoided"},
|
|
)
|
|
return result
|
|
|
|
def unvoid_transaction(self, doc_code, doc_type):
|
|
self.ensure_one()
|
|
result = False
|
|
if not self.disable_tax_reporting:
|
|
avatax = self.get_avatax_rest_service()
|
|
result = avatax.call("unvoid_transaction", self.company_code, doc_code)
|
|
return result
|
|
|
|
def ping(self):
|
|
client = AvaTaxRESTService(config=self)
|
|
client.ping()
|
|
return True
|