Files
enterprise-addons/l10n_ar_edi/models/res_company.py
T

161 lines
9.2 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools.misc import file_open
from datetime import datetime
import base64
import random
import logging
_logger = logging.getLogger(__name__)
class ResCompany(models.Model):
_inherit = "res.company"
l10n_ar_afip_verification_type = fields.Selection([('not_available', 'Not Available'), ('available', 'Available'), ('required', 'Required')], required=True,
default='not_available', string='ARCA Invoice Verification', help='It adds an option on invoices to'
' verify the invoices in ARCA if the invoices has CAE, CAI or CAEA numbers.\n\n'
'* Not Available: Will NOT show "Verify on ARCA" button in the invoices\n'
'* Available: Will show "Verify on ARCA" button in the invoices so the user can manually check the vendor'
' bills\n'
'* Required: The vendor bills will be automatically verified on ARCA before been posted in Odoo. This is to'
' ensure that you have verified all the vendor bills that you are reporting in your Purchase VAT Book. NOTE:'
' Not all the document types can be validated in ARCA, only the ones defined in this link are the ones that '
' we are automatically validating https://serviciosweb.afip.gob.ar/genericos/comprobantes/Default.aspx')
l10n_ar_connection_ids = fields.One2many('l10n_ar.afipws.connection', 'company_id', 'Connections')
# Certificate fields
l10n_ar_afip_ws_environment = fields.Selection([('testing', 'Testing'), ('production', 'Production')], string="ARCA Environment", default='production',
help="Environment used to connect to ARCA webservices. Production is to create real fiscal invoices in ARCA,"
" Testing is for testing invoice creation in ARCA (commonly named in ARCA as Homologation environment).")
l10n_ar_afip_ws_key_id = fields.Many2one(string='Private Key', comodel_name='certificate.key', domain=[('public', '=', False)],
compute="_compute_afip_key", store=True, readonly=False,
help="This private key is required because is sent to the ARCA when"
" trying to create a connection to validate that you are you\n\n * If you have one you can upload it here (In"
" order to be valid the private key should be in PEM format)\n * if you have not then Odoo will automatically"
" create a new one when you click in 'Generate Request' or 'Generate Renewal Request' button")
l10n_ar_afip_ws_crt_id = fields.Many2one(string='ARCA Certificate', comodel_name="certificate.certificate",
compute="_compute_afip_crt", store=True, readonly=False,
help="This certificate lets us connect to ARCA to validate electronic invoice."
" Please select here the ARCA certificate in PEM format. You can get your certificate from your ARCA Portal")
l10n_ar_fce_transmission_type = fields.Selection(
[('SCA', 'SCA - TRANSFERENCIA AL SISTEMA DE CIRCULACION ABIERTA'), ('ADC', 'ADC - AGENTE DE DEPOSITO COLECTIVO')],
'FCE: Transmission Option Default',
help='Default value for "FCE: Transmission Option" on electronic invoices')
l10n_ar_payment_foreign_currency = fields.Selection(
selection=[("Yes", "Yes"), ("No", "No"), ("account", "Account's Currency Dependant")],
compute="_compute_l10n_ar_payment_foreign_currency",
string="Default Policy for Payment in Foreign Currency",
)
l10n_ar_show_withholding_legend = fields.Boolean(
string='Show Withholding Legend',
help="If this option is selected, the legend 'Operation Subject to Withholding' will be added below the Document Type letter on the Invoice PDF."
)
def _compute_l10n_ar_payment_foreign_currency(self):
for company in self:
company.l10n_ar_payment_foreign_currency = self.env["ir.config_parameter"].sudo().get_param(
f"l10n_ar_edi.{company.id}_foreign_currency_payment", "No")
@api.depends('l10n_ar_afip_ws_key_id')
def _compute_afip_crt(self):
key_ids = self.l10n_ar_afip_ws_key_id.ids
certs = self.env['certificate.certificate'].search([('private_key_id', 'in', key_ids)])
key_to_cert = {cert.private_key_id: cert for cert in certs}
key_to_cert[False] = False
for company in self:
if company.country_code != 'AR':
continue
if not company.l10n_ar_afip_ws_crt_id:
company.l10n_ar_afip_ws_crt_id = key_to_cert.get(company.l10n_ar_afip_ws_key_id)
else:
company.l10n_ar_afip_ws_crt_id.private_key_id = company.l10n_ar_afip_ws_key_id
@api.depends('l10n_ar_afip_ws_crt')
def _compute_l10n_ar_afip_ws_crt_fname(self):
""" Set the certificate name in the company. Needed in unit tests, solved by a similar onchange method in res.config.settings while setting the certificate via web interface """
with_crt = self.filtered(lambda x: x.l10n_ar_afip_ws_crt)
remaining = self - with_crt
for rec in with_crt:
certificate = self._l10n_ar_get_certificate_object(rec.with_context(bin_size=False).l10n_ar_afip_ws_crt)
rec.l10n_ar_afip_ws_crt_fname = certificate.get_subject().CN
remaining.l10n_ar_afip_ws_crt_fname = ''
@api.depends('l10n_ar_afip_ws_crt_id.private_key_id')
def _compute_afip_key(self):
for company in self:
if company.country_code != 'AR':
continue
company.l10n_ar_afip_ws_key_id = company.l10n_ar_afip_ws_crt_id.private_key_id
def _get_environment_type(self):
""" This method is used to return the environment type of the company (testing or production) and will raise an
exception when it has not been defined yet """
self.ensure_one()
if not self.l10n_ar_afip_ws_environment:
raise UserError(_('ARCA environment not configured for company “%s”, please check accounting settings', self.name))
return self.l10n_ar_afip_ws_environment
def _l10n_ar_get_connection(self, afip_ws):
""" Returns the last existing connection with ARCA web service, or creates a new one (which means login to ARCA
and save token information in a new connection record in Odoo)
IMPORTANT WARNING: Be careful using this method, when a new connection is created, it will do a cr.commit() """
self.ensure_one()
if not afip_ws:
raise UserError(_('No ARCA WS selected'))
env_type = self._get_environment_type()
connection = self.l10n_ar_connection_ids.search([('type', '=', env_type), ('l10n_ar_afip_ws', '=', afip_ws), ('company_id', '=', self.id)], limit=1)
if connection and connection.expiration_time > fields.Datetime.now():
return connection
token_data = connection._l10n_ar_get_token_data(self, afip_ws)
if connection:
connection.sudo().write(token_data)
else:
values = {'company_id': self.id, 'l10n_ar_afip_ws': afip_ws, 'type': env_type}
values.update(token_data)
_logger.info('Connection created for company %s %s (%s)' % (self.name, afip_ws, env_type))
connection = connection.sudo().create(values)
# This commit is needed because we need to maintain the connection information no matter if the invoices have
# been validated or not. This because when we request a token we can not generate a new one until the last
# one expires.
if not self.env.context.get('l10n_ar_invoice_skip_commit'):
self.env.cr.commit()
_logger.info("Successful Authenticated with ARCA.")
return connection
def set_demo_random_cert(self):
""" Method used to assign a random certificate to the company. This method is called when loading demo data to
assign a random certificate to the demo companies. It is also available as button in the res.config.settings
wizard to let the user change the certificate randomly if the one been set is blocked (because someone else
is using the same certificate in another database) """
def read_datas(path):
with file_open(path, 'rb') as f:
return base64.b64encode(f.read())
for company in self:
if company.country_code != 'AR':
continue
old_cert_name = company.l10n_ar_afip_ws_crt_id.name
rid = random.randint(1, 10)
new_cert_name = 'AR demo certificate %d' % rid
new_cert = self.env['certificate.certificate'].search([('name', '=', new_cert_name), ('company_id', '=', company.id)], limit=1)
if not new_cert:
cert_file = 'l10n_ar_edi/demo/cert%d.crt' % rid
new_cert = self.env['certificate.certificate'].create({
'name': new_cert_name,
'content': read_datas(cert_file),
'private_key_id': company.l10n_ar_afip_ws_key_id.id,
'company_id': company.id,
})
company.l10n_ar_afip_ws_crt_id = new_cert
_logger.log(25, 'Setting demo certificate from %s to %s in %s company', old_cert_name, new_cert_name, company.name)