Skip to content

Commit

Permalink
Merge PR #3683 into 17.0
Browse files Browse the repository at this point in the history
Signed-off-by pedrobaeza
  • Loading branch information
OCA-git-bot committed Sep 20, 2024
2 parents 23c934f + 1d84a77 commit a05e17a
Show file tree
Hide file tree
Showing 41 changed files with 3,197 additions and 0 deletions.
172 changes: 172 additions & 0 deletions l10n_es_facturae_face/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
========================
Envío de Facturae a FACe
========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cb5acc786d5d80c239ec646278ab331b1c28813e7d219a9e85a8a8294ed9717d
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--spain-lightgray.png?logo=github
:target: https://github.com/OCA/l10n-spain/tree/17.0/l10n_es_facturae_face
:alt: OCA/l10n-spain
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/l10n-spain-17-0/l10n-spain-17-0-l10n_es_facturae_face
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-spain&target_branch=17.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Este módulo permite la gestión del envío de la facturación electrónica
española a FACe. La gestión del envío se realiza mediante los
certificados con los que se firma.

**Table of contents**

.. contents::
:local:

Configuration
=============

- Es necesario añadir el correo electrónico al que notificar los
cambios de estado en la empresa
- Se debe configurar el servidor de envío
- Por defecto se añado el servicio web de test:
https://se-face-webservice.redsara.es/facturasspp2
- Si queremos añadir el de producción, debemos cambiar el parámetro por
https://webservice.face.gob.es/facturasspp2 y modificar el
certificado en parámetros de sistema

Para poder enviar correctamente, debemos subir el certificado al entorno
correspondiente, para ello, accederemos a https://face.gob.es
(Producción) o https://se-face.redsara.es (Desarrollo). Allí,
accederemos a Integradores/Gestión de certificados y nos loguearemos con
Certificado Electrónico. Una vez dentro, debemos darnos de alta como
integrador creando una incidencia en la URL que nos aparecerá. El
siguiente
`documento <https://administracionelectronica.gob.es/PAe/FACE/altaintegrador>`__
tiene toda la información detallada.

Cuando nos confirmen el alta, será necesario subir la parte pública de
nuestro certificado, un comando para exportarlo es:

.. code:: bash
openssl pkcs12 -in MI_CERTIFICADO.pfx -out MI_CERTIFICADO.crt -nokeys -clcerts
cat MI_CERTIFICADO.crt
Deberemos añadir toda la parte entre -----BEGIN CERTIFICATE----- y
-----END CERTIFICATE----- incluidos ambos.

Usage
=====

Configurar clientes
-------------------

- Accedemos a un cliente

- Le configuramos la opción de factura electrónica y rellenamos los
datos obligatorios
- Marcamos como método de envío FACe

- A partir de este momento, todas las facturas que validemos del
cliente se enviarán automáticamente a no ser que marquemos la opción
de Deshabilitar envío EDI

Envío de facturas
-----------------

- Cuando validemos una factura de un cliente configurado a enviar por
FACe se creará un registro de Envío EDI
- Mediante un job, se generarán los datos necesarios y, posteriormente,
se enviará como un registro EDI estándar
- Una vez se envíe, se alamacenará en la factura el resultado y el
número de registro
- Tras eso, podremos actualizar el estado de forma online presionando
el botón Actualizar Estado FACe
- Además, también podremos solicitar la anulación de la factura
pulsando 'Cancelar Envío' e introduciendo el motivo

Es importante tener en cuenta que:

- Un registro Enviado correctamente no puede ser Eliminado
- Sólo puede existir un envío Enviado correctamente (no cancelado)
- Se genera una tarea programada que actualiza los registros enviados
correctamente no pagados y no anulados
- En caso de que se anule la factura por parte del cliente, podremos
reenviarla de nuevo

Envío manual de facturas
------------------------

- Esto podría pasarnos con facturas antiguas en las que configuramos el
cliente tras emitir la factura o en las que hemos deshabilitado el
envío automático
- Accedemos a una factura validada del cliente no enviada y pulsamos el
botón 'Spanish Facturae'. En caso de salirnos una opción de elección,
deberemos seleccionara FACe
- Tras esto, funcionará de la misma forma que un envío estándar

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/l10n-spain/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/l10n-spain/issues/new?body=module:%20l10n_es_facturae_face%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Creu Blanca

Contributors
------------

- Enric Tobella <etobella@creublanca.es>
- Eric Antones <eantones@nuobit.com>
- Manuel Regidor <manuel.regidor@sygel.es>
- Valentín Vinagre <valentin.vinagre@sygel.es>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-etobella| image:: https://github.com/etobella.png?size=40px
:target: https://github.com/etobella
:alt: etobella

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-etobella|

This module is part of the `OCA/l10n-spain <https://github.com/OCA/l10n-spain/tree/17.0/l10n_es_facturae_face>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
5 changes: 5 additions & 0 deletions l10n_es_facturae_face/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import components
from . import models
from . import wizards
30 changes: 30 additions & 0 deletions l10n_es_facturae_face/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# © 2017 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

{
"name": "Envío de Facturae a FACe",
"version": "17.0.1.0.0",
"author": "Creu Blanca, Odoo Community Association (OCA)",
"category": "Accounting & Finance",
"website": "https://github.com/OCA/l10n-spain",
"license": "AGPL-3",
"depends": [
"l10n_es_facturae",
"edi_account_oca",
"edi_exchange_template_oca",
],
"data": [
"security/ir.model.access.csv",
"data/edi.xml",
"data/face_data.xml",
"data/cron_data.xml",
"wizards/edi_l10n_es_facturae_face_cancel.xml",
"views/account_move.xml",
"views/res_company_view.xml",
"views/res_partner.xml",
"views/edi_exchange_record.xml",
],
"external_dependencies": {"python": ["zeep", "cryptography==3.4.8"]},
"installable": True,
"maintainers": ["etobella"],
}
7 changes: 7 additions & 0 deletions l10n_es_facturae_face/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from . import webservice_face
from . import account_move_l10n_es_facturae_listener
from . import edi_send_l10n_es_facturae_face
from . import account_move_l10n_es_facturae_face_listener
from . import edi_webservice_receive_face_l10n_es_facturae_face_update
from . import edi_input_process_l10n_es_facturae_l10n_es_facturae_face_update
from . import edi_send_l10n_es_facturae_face_cancel
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2020 Creu Blanca
# @author: Enric Tobella
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _
from odoo.exceptions import UserError

from odoo.addons.component.core import Component


class AccountMoveL10nEsFacturaeFACeListener(Component):
_name = "account.move.l10n.es.facturae.face.listener"
_inherit = "base.event.listener"
_apply_on = ["account.move"]

def on_edi_generate_manual(self, move, exchange_record):
if exchange_record.type_id.code != "l10n_es_facturae_face_update":
return
related_record = move._get_exchange_record(
self.env.ref("l10n_es_facturae_face.facturae_exchange_type"),
self.env.ref("l10n_es_facturae_face.face_backend"),
)
if not related_record:
raise UserError(_("Exchange record cannot be found for FACe"))
if exchange_record.edi_exchange_state == "new":
exchange_record.write(
{"edi_exchange_state": "input_pending", "parent_id": related_record.id}
)
exchange_record.backend_id.with_context(
_edi_send_break_on_error=True
).exchange_receive(exchange_record)
exchange_record.backend_id.with_context(
_edi_send_break_on_error=True
).exchange_process(exchange_record)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2020 Creu Blanca
# @author: Enric Tobella
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.addons.component.core import Component


class AccountMoveL10nEsFacturaeListener(Component):
_name = "account.move.l10n.es.facturae.listener"
_inherit = "base.event.listener"
_apply_on = ["account.move"]

def _get_backend(self, record):
return self.env.ref("l10n_es_facturae_face.face_backend")

def _get_exchange_record_vals(self, record):
return {
"model": record._name,
"res_id": record.id,
}

def on_post_account_move(self, records):
for record in records:
if record.edi_disable_auto:
continue
partner = record.partner_id
if record.move_type not in ["out_invoice", "out_refund"]:
continue
if not partner.facturae or not partner.l10n_es_facturae_sending_code:
continue
backend = self._get_backend(record)
if not backend:
continue
exchange_type = self.env.ref("l10n_es_facturae_face.facturae_exchange_type")
# We check fields now to raise an error to the user, otherwise the
# error will be raising silently in the queue job.
record.validate_facturae_fields()
if record._has_exchange_record(exchange_type, backend):
continue
exchange_record = backend.create_record(
exchange_type.code, self._get_exchange_record_vals(record)
)
exchange_record.with_delay().action_exchange_generate()

def on_generate_account_edi(self, records):
return self.on_post_account_move(records)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2020 Creu Blanca
# @author: Enric Tobella
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import json

from odoo.addons.component.core import Component


class EdiInputProcessL10nEsFacturaeFaceUpdate(Component):
_name = "edi.input.process.l10n_es_facturae.l10n_es_facturae_face_update"
_usage = "input.process"
_backend_type = "l10n_es_facturae"
_exchange_type = "l10n_es_facturae_face_update"

_inherit = "edi.component.input.mixin"

def process(self):
data = json.loads(self.exchange_record._get_file_content())
parent = self.exchange_record.parent_id
process_code = "face-" + data["tramitacion"]["codigo"]
revocation_code = "face-" + data["anulacion"]["codigo"]
if (
process_code == parent.l10n_es_facturae_status
and revocation_code == parent.l10n_es_facturae_cancellation_status
):
return
parent.write(
{
"l10n_es_facturae_status": process_code,
"l10n_es_facturae_motive": data["tramitacion"]["motivo"],
"l10n_es_facturae_cancellation_status": revocation_code,
"l10n_es_facturae_cancellation_motive": data["anulacion"]["motivo"],
}
)
self.exchange_record.record.write(
{
"l10n_es_facturae_status": process_code,
"l10n_es_facturae_cancellation_status": revocation_code,
}
)
33 changes: 33 additions & 0 deletions l10n_es_facturae_face/components/edi_send_l10n_es_facturae_face.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2020 Creu Blanca
# @author: Enric Tobella
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.addons.component.core import Component


class EdiOutputSendL10nEsFacturaeFace(Component):
_name = "edi.output.send.l10n_es_facturae.l10n_es_facturae_face_output"
_usage = "output.send"
_backend_type = "l10n_es_facturae"
_exchange_type = "l10n_es_facturae"
_inherit = ["edi.component.send.mixin", "base.webservice.face"]

def _get_extra_attachment(self):
return []

def send(self):
invoice = self.exchange_record.record
public_crt, private_key = self.env["l10n.es.aeat.certificate"].get_certificates(
invoice.company_id
)
response = self.send_webservice(
public_crt,
private_key,
self.exchange_record._get_file_content(),
self.exchange_record.exchange_filename,
invoice.company_id.face_email,
self._get_extra_attachment(),
)
self.exchange_record.write(
{"external_identifier": response.factura.numeroRegistro}
)
Loading

0 comments on commit a05e17a

Please sign in to comment.