Skip to content

Commit

Permalink
[IMP] l10n_es_aeat: use certificate module
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronHForgeFlow committed Feb 25, 2025
1 parent ee6fcfb commit dcd4311
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 304 deletions.
6 changes: 6 additions & 0 deletions l10n_es_aeat/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ Known issues / Roadmap
auto-selecciona por fechas de validez.
- Las partes específicas de las Diputaciones Forales no están
incluidas.
- El módulo de certificate no incluye la funcionalidad de especificar
la carpeta dónde se almacena el certificado, se opta por eliminar la
funcionalidad en v18
- El módulo de certificate guarda el password en el certificado, esa
información no está disponible antes de migrar por lo que se dejará
vacía

Bug Tracker
===========
Expand Down
3 changes: 1 addition & 2 deletions l10n_es_aeat/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"website": "https://github.com/OCA/l10n-spain",
"category": "Accounting & Finance",
"development_status": "Mature",
"depends": ["l10n_es", "account_tax_balance"],
"depends": ["l10n_es", "account_tax_balance", "certificate"],
# odoo_test_helper is needed for the tests
"external_dependencies": {"python": ["unidecode"]},
"data": [
Expand All @@ -33,7 +33,6 @@
"data/aeat_tax_agency_data.xml",
"wizard/export_to_boe_wizard.xml",
"wizard/compare_boe_file_views.xml",
"wizard/aeat_certificate_password_view.xml",
"views/aeat_menuitem.xml", # it should be before the other views
"views/aeat_map_tax_views.xml",
"views/aeat_report_view.xml",
Expand Down
64 changes: 64 additions & 0 deletions l10n_es_aeat/migrations/18.0.1.0.0/post-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import base64
import binascii
import json
import logging

from odoo import SUPERUSER_ID, api

_logger = logging.getLogger(__name__)


def migrate(cr, version):
"""Restores AEAT certificate data into the new schema."""
env = api.Environment(cr, SUPERUSER_ID, {})
migration_data = env["ir.config_parameter"].get_param(
"l10n_es_aeat_certificate_migration_data"
)
if not migration_data:
raise ValueError("No AEAT certificate migration data found")

migration_data = json.loads(migration_data)

for cert_id, cert_data in migration_data.items():
private_key_data = cert_data.get("private_key", "")
date_start = cert_data.get("date_start")
date_end = cert_data.get("date_end")
public_key_path = cert_data.get("public_key", "")
try:
private_key_bytes = private_key_data.encode() if private_key_data else None
except binascii.Error:
private_key_bytes = None
if public_key_path:
try:
with open(public_key_path, "rb") as f:
public_key_content = f.read()
except FileNotFoundError:
_logger.info("Public key file not found")
key_id = (
env["certificate.key"].create(
{
"name": f"Key for AEAT Cert {cert_id}",
"content": private_key_bytes or b"",
}
)
if private_key_bytes
else None
)
cert_id_new = env["certificate.certificate"].create(
{
"name": f"AEAT Cert {cert_id}",
"private_key_id": key_id.id if key_id else False,
"date_start": date_start,
"date_end": date_end,
"content": base64.b64encode(public_key_content).decode()
if public_key_content
else "",
}
)
env["l10n.es.aeat.certificate"].browse(int(cert_id)).write(
{"certificate_id": cert_id_new.id}
)
# Clean up stored migration data
env["ir.config_parameter"].sudo().set_param(
"l10n_es_aeat_certificate_migration_data", ""
)
50 changes: 50 additions & 0 deletions l10n_es_aeat/migrations/18.0.1.0.0/pre-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import base64
import json

from openupgradelib import openupgrade


def _save_certificates(env):
cr = env.cr
cr.execute("""
SELECT id, company_id, name, state, date_start, date_end,
public_key, private_key
FROM l10n_es_aeat_certificate
""")
certificates = cr.fetchall()
stored_data = {}
for cert in certificates:
(
cert_id,
company_id,
name,
state,
date_start,
date_end,
public_key,
private_key_path,
) = cert
private_key_content = ""
if private_key_path:
try:
with open(private_key_path, "rb") as f:
private_key_content = base64.b64encode(f.read()).decode()
except FileNotFoundError:
private_key_content = ""
stored_data[str(cert_id)] = {
"company_id": company_id,
"name": name,
"state": state,
"date_start": str(date_start) if date_start else None,
"date_end": str(date_end) if date_end else None,
"public_key": public_key or "",
"private_key": private_key_content,
}
env["ir.config_parameter"].set_param(
"l10n_es_aeat_certificate_migration_data", json.dumps(stored_data)
)


@openupgrade.migrate()
def migrate(env, version):
_save_certificates(env)
54 changes: 20 additions & 34 deletions l10n_es_aeat/models/aeat_certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,18 @@ class L10nEsAeatCertificate(models.Model):
selection=[("draft", "Draft"), ("active", "Active")],
default="draft",
)
file = fields.Binary(required=True)
folder = fields.Char(string="Folder Name", required=True)
date_start = fields.Date(string="Start Date")
date_end = fields.Date(string="End Date")
public_key = fields.Char(readonly=True)
private_key = fields.Char(readonly=True)
certificate_id = fields.Many2one(
comodel_name="certificate.certificate",
string="Certificate",
)
company_id = fields.Many2one(
comodel_name="res.company",
string="Company",
required=True,
default=lambda self: self.env.company,
)

def load_password_wizard(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"name": self.env._("Insert Password"),
"res_model": "l10n.es.aeat.certificate.password",
"view_mode": "form",
"views": [(False, "form")],
"target": "new",
}
date_start = fields.Datetime(related="certificate_id.date_start")
date_end = fields.Datetime(related="certificate_id.date_end")

def action_active(self):
self.ensure_one()
Expand All @@ -55,28 +44,25 @@ def get_certificates(self, company=False):
aeat_certificate = self.search(
[
("company_id", "=", company.id),
("public_key", "!=", False),
("private_key", "!=", False),
("certificate_id", "!=", False),
"|",
("date_start", "=", False),
("date_start", "<=", today),
("certificate_id.date_start", "=", False),
("certificate_id.date_start", "<=", today),
"|",
("date_end", "=", False),
("date_end", ">=", today),
("certificate_id.date_end", "=", False),
("certificate_id.date_end", ">=", today),
("state", "=", "active"),
],
limit=1,
)
if aeat_certificate:
public_crt = aeat_certificate.public_key
private_key = aeat_certificate.private_key
else:
public_crt = self.env["ir.config_parameter"].get_param(
"l10n_es_aeat_certificate.publicCrt", False
)
private_key = self.env["ir.config_parameter"].get_param(
"l10n_es_aeat_certificate.privateKey", False
)
if not public_crt or not private_key:
if not aeat_certificate:
raise exceptions.UserError(self.env._("Error! There aren't certificates."))

public_crt = aeat_certificate.certificate_id.pem_certificate
private_key_record = aeat_certificate.certificate_id.private_key_id
if not private_key_record or not private_key_record.pem_key:
raise exceptions.UserError(self.env._("Private key is missing or invalid."))

private_key = private_key_record.pem_key

return public_crt, private_key
23 changes: 17 additions & 6 deletions l10n_es_aeat/models/aeat_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Copyright 2023-2024 Aures Tic - Jose Zambudio <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import tempfile

from requests import Session

Expand Down Expand Up @@ -138,12 +139,22 @@ def _connect_aeat(self, mapping_key):
company=self.company_id
)
params = self._connect_params_aeat(mapping_key)
session = Session()
session.cert = (public_crt, private_key)
transport = Transport(session=session)
history = HistoryPlugin()
client = Client(wsdl=params["wsdl"], transport=transport, plugins=[history])
return self._bind_service(client, params["port_name"], params["address"])
# Create temporary files to store the certificate and key
with (
tempfile.NamedTemporaryFile(delete=False, suffix=".crt") as cert_file,
tempfile.NamedTemporaryFile(delete=False, suffix=".key") as key_file,
):
cert_file.write(public_crt)
key_file.write(private_key)
cert_file.flush()
key_file.flush()
# Set up session with certificate and key file paths
session = Session()
session.cert = (cert_file.name, key_file.name) # Provide file paths
transport = Transport(session=session)
history = HistoryPlugin()
client = Client(wsdl=params["wsdl"], transport=transport, plugins=[history])
return self._bind_service(client, params["port_name"], params["address"])

def _get_aeat_country_code(self):
self.ensure_one()
Expand Down
4 changes: 4 additions & 0 deletions l10n_es_aeat/readme/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
- La configuración de exportación a BOE no se filtran ni se
auto-selecciona por fechas de validez.
- Las partes específicas de las Diputaciones Forales no están incluidas.
- El módulo de certificate no incluye la funcionalidad de especificar la carpeta
dónde se almacena el certificado, se opta por eliminar la funcionalidad en v18
- El módulo de certificate guarda el password en el certificado, esa información
no está disponible antes de migrar por lo que se dejará vacía
1 change: 0 additions & 1 deletion l10n_es_aeat/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@ access_l10n_es_aeat_soap,access_l10n_es_aeat_soap,model_l10n_es_aeat_soap,group_
access_l10n_es_aeat_report_compare_boe_file,access_l10n_es_aeat_report_compare_boe_file,model_l10n_es_aeat_report_compare_boe_file,group_account_aeat,1,1,1,0
access_l10n_es_aeat_report_compare_boe_file_line,access_l10n_es_aeat_report_compare_boe_file_line,model_l10n_es_aeat_report_compare_boe_file_line,group_account_aeat,1,1,1,0
access_l10n_es_aeat_report_export_to_boe,access_l10n_es_aeat_report_export_to_boe,model_l10n_es_aeat_report_export_to_boe,group_account_aeat,1,1,1,0
access_l10n_es_aeat_certificate_password,access_l10n_es_aeat_certificate_password,model_l10n_es_aeat_certificate_password,group_account_aeat,1,1,1,0
6 changes: 6 additions & 0 deletions l10n_es_aeat/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,12 @@ <h1><a class="toc-backref" href="#toc-entry-4">Known issues / Roadmap</a></h1>
auto-selecciona por fechas de validez.</li>
<li>Las partes específicas de las Diputaciones Forales no están
incluidas.</li>
<li>El módulo de certificate no incluye la funcionalidad de especificar
la carpeta dónde se almacena el certificado, se opta por eliminar la
funcionalidad en v18</li>
<li>El módulo de certificate guarda el password en el certificado, esa
información no está disponible antes de migrar por lo que se dejará
vacía</li>
</ul>
</div>
<div class="section" id="bug-tracker">
Expand Down
Loading

0 comments on commit dcd4311

Please sign in to comment.