155 lines
6.9 KiB
Python
155 lines
6.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import stdnum
|
|
|
|
from json import JSONDecodeError
|
|
from pprint import pformat
|
|
|
|
from odoo import fields, models, api, _
|
|
from odoo.exceptions import UserError, AccessError, RedirectWarning, ValidationError
|
|
from odoo.tools.safe_eval import json
|
|
|
|
|
|
class ResConfigSettings(models.TransientModel):
|
|
_inherit = 'res.config.settings'
|
|
|
|
l10n_br_avatax_portal_email = fields.Char(
|
|
related='company_id.l10n_br_avatax_portal_email',
|
|
string='Avatax Portal Email',
|
|
readonly=False,
|
|
)
|
|
l10n_br_avatax_show_overwrite_warning = fields.Boolean(
|
|
compute='_compute_show_overwrite_warning',
|
|
store=False,
|
|
help="Technical field used to determine whether or not the user is about to overwrite his current API credentials"
|
|
"with new ones, since the old credentials won't be recoverable we warn."
|
|
)
|
|
l10n_br_avatax_api_identifier = fields.Char(
|
|
related='company_id.l10n_br_avatax_api_identifier',
|
|
readonly=False,
|
|
string='Avalara Brazil API ID'
|
|
)
|
|
l10n_br_avatax_api_key = fields.Char(
|
|
related='company_id.l10n_br_avatax_api_key',
|
|
readonly=False,
|
|
string='Avalara Brazil API Key'
|
|
)
|
|
l10n_br_avalara_environment = fields.Selection(
|
|
related='company_id.l10n_br_avalara_environment',
|
|
readonly=False,
|
|
string="Avalara Brazil Environment",
|
|
required=True,
|
|
)
|
|
l10n_br_icms_rate = fields.Float(
|
|
related='company_id.l10n_br_icms_rate',
|
|
string='Simplified Regime ICMS Rate',
|
|
help="This only applies if you are a simplified tax regime company.",
|
|
readonly=False,
|
|
)
|
|
l10n_br_tax_regime = fields.Selection(related='company_id.partner_id.l10n_br_tax_regime')
|
|
l10n_br_cnae_code_id = fields.Many2one(
|
|
related='company_id.l10n_br_cnae_code_id',
|
|
help="Main CNAE code registered with the government.",
|
|
readonly=False,
|
|
)
|
|
l10n_br_avatax_show_existing_account_warning = fields.Boolean(compute='_compute_l10n_br_avalara_account_count')
|
|
|
|
def _compute_l10n_br_avalara_account_count(self):
|
|
companies_with_avatax_account = self.env['res.company'].search_count([
|
|
('l10n_br_avatax_portal_email', '!=', False),
|
|
('l10n_br_avatax_api_identifier', '!=', False),
|
|
('l10n_br_avatax_api_key', '!=', False),
|
|
])
|
|
self.l10n_br_avatax_show_existing_account_warning = companies_with_avatax_account > 0
|
|
|
|
@api.depends('l10n_br_avalara_environment', 'l10n_br_avatax_api_identifier', 'l10n_br_avatax_api_key')
|
|
def _compute_show_overwrite_warning(self):
|
|
for settings in self:
|
|
settings.l10n_br_avatax_show_overwrite_warning = bool(settings.l10n_br_avatax_api_identifier)
|
|
|
|
def _validate_create_account_data(self):
|
|
""" Raises actionable errors in case there is missing required data. """
|
|
partner = self.company_id.partner_id
|
|
if not self.l10n_br_avatax_portal_email:
|
|
# Don't redirect, the user is already on in the Accounting settings.
|
|
raise ValidationError(_("Please set a valid Avatax portal email."))
|
|
|
|
if not partner.vat:
|
|
raise RedirectWarning(
|
|
_("Please set a valid Tax ID on your company."),
|
|
partner._get_records_action(),
|
|
_("Go to company configuration")
|
|
)
|
|
|
|
required_address_fields = ("street_name", "street2", "street_number", "zip")
|
|
for field in required_address_fields:
|
|
if not partner[field]:
|
|
raise RedirectWarning(
|
|
_("Please set a complete address on your company."),
|
|
partner._get_records_action(),
|
|
_("Go to company configuration")
|
|
)
|
|
|
|
def create_account(self):
|
|
""" This gathers all metadata needed to create an account, does the request to the IAP server and parses
|
|
the response. """
|
|
self._validate_create_account_data()
|
|
partner = self.company_id.partner_id
|
|
payload = {
|
|
'subscriptionName': self.company_name,
|
|
'corporateName': self.company_name,
|
|
'tradeName': self.company_name,
|
|
'cnpj': stdnum.util.get_cc_module('br', 'vat').format(partner.vat or ''),
|
|
'municipalRegistration': partner.l10n_br_im_code,
|
|
'stateRegistration': partner.l10n_br_ie_code,
|
|
'suframa': partner.l10n_br_isuf_code,
|
|
'address': partner.street_name,
|
|
'neighborhood': partner.street2,
|
|
'addressNumber': partner.street_number,
|
|
'corporateContactEmailAddress': self.l10n_br_avatax_portal_email,
|
|
'zipCode': partner.zip,
|
|
}
|
|
result = self.env['account.external.tax.mixin']._l10n_br_iap_create_account({k: v or '' for k, v in payload.items()}, self.company_id)
|
|
|
|
if 'avalara_api_id' in result:
|
|
self.company_id.l10n_br_avatax_api_identifier = result['avalara_api_id']
|
|
self.company_id.l10n_br_avatax_api_key = result['avalara_api_key']
|
|
else:
|
|
# API returns errors either as a string containing JSON:
|
|
# {'message': '{"errors":{"Login do usuário master":["Login já utlizado"]},"title":"One or more validation errors occurred.","status":400,"traceId":"0HMPVCEB27KLU:000000E5"}', 'isError': True}
|
|
# Or as a regular string:
|
|
# {'message': 'An unhandled error occurred. Trace ID: xxx', 'isError': True}
|
|
if 'message' in result:
|
|
try:
|
|
result = json.loads(result['message'])
|
|
except JSONDecodeError:
|
|
if 'unhandled error occurred' in result['message']:
|
|
raise UserError(_('The Avatax platform failed to create your account. Please ensure the address on your company is correct. If it is please contact support at odoo.com/help.'))
|
|
else:
|
|
raise UserError(result['message'])
|
|
|
|
if errors := result.get('errors'):
|
|
raise UserError('\n'.join(
|
|
'{}:\n{}'.format(
|
|
key,
|
|
"\n".join(f" - {error}" for error in errs)
|
|
)
|
|
for key, errs in errors.items()
|
|
))
|
|
|
|
def button_l10n_br_avatax_ping(self):
|
|
if not self.env.is_system():
|
|
raise AccessError(_('Only administrators can ping Avatax.'))
|
|
|
|
query_result = self.env['account.external.tax.mixin']._l10n_br_iap_ping(self.company_id)
|
|
raise RedirectWarning(
|
|
_("Server Response:\n%s", pformat(query_result)),
|
|
action=self.env.ref('account.action_account_config').id,
|
|
button_text=_("Continue Configurations")
|
|
)
|
|
|
|
def button_l10n_br_avatax_log(self):
|
|
return self.env['account.external.tax.mixin']._l10n_br_avatax_log()
|
|
|
|
def button_l10n_br_avatax_open_company_partner(self):
|
|
return self.company_id.partner_id._get_records_action()
|