Skip to content

Commit

Permalink
l10n_fr_das2: update for millesime 2024 using pyfrdas2
Browse files Browse the repository at this point in the history
  • Loading branch information
alexis-via committed Mar 11, 2024
1 parent fc7eb8a commit 450d8f0
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 259 deletions.
111 changes: 1 addition & 110 deletions l10n_fr_das2/README.rst
Original file line number Diff line number Diff line change
@@ -1,110 +1 @@
====
DAS2
====

.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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--france-lightgray.png?logo=github
:target: https://github.com/OCA/l10n-france/tree/14.0/l10n_fr_das2
:alt: OCA/l10n-france
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/l10n-france-14-0/l10n-france-14-0-l10n_fr_das2
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/121/14.0
:alt: Try me on Runbot

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

This module adds support for `DAS2 <https://www.impots.gouv.fr/portail/formulaire/das2/etat-des-honoraires-vacations-commissions-courtages-ristournes-et-jetons>_`, which is an annual fiscal declaration also called *Déclaration d'honoraires*. It will allow you to auto-generate the lines of DAS2 from the accounting entries, check the result, manually update lines if needed and eventually generate a declaration file. Then send the declaration file to the French tax office via your professional account on *impots.gouv.fr*

The specifications of the file are available on `this page <https://www.impots.gouv.fr/les-cahiers-des-charges-tdbilateral>`_.

**Table of contents**

.. contents::
:local:

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

On the supplier form view, in the *Accounting* tab, you will see a section *DAS2*. For the suppliers that must be declared in DAS2, you must set:

* the DAS2 Type,
* the job for the DAS2 declaration,
* their SIRET number (for French suppliers only),
* their full address (street, zip code, city and country).

On the company configuration form, the APE code, SIRET and address must be set.

For the user responsible for the declaration, the phone number and email must be set on his related partner form (name, email and phone number are used in the DAS2 declaration file).

Usage
=====

Go to the menu *Accounting > Reports > French Statements > DAS2* and create a new DAS2 report.

Then click on the button *Generate Lines*. Check and edit the generated lines. You can get the details of the computation performed by Odoo in the *Note* fields of each line.

You may have a yellow warning banner that warn you about suppliers that have expenses recorded in accounts such as 622100 Commissions et courtages sur achats, 622200 Commissions et courtages sur ventes, 622600 Honoraires, 622800 Rémunérations d'intermédiaires divers, 653000 Jetons de présence, 651600 Droits d'auteur et de reproduction that are not configured for DAS2.

Once your declaration is OK, click on the button *Done*: it will generate the DAS2 file and set the declaration to *Done* state (all the fields become readonly).

To send the file to the French tax office, go to your professional account on `impots.gouv.fr <https://www.impots.gouv.fr/>`_, go to the menu **Déclarer > Tiers déclarant** and then click on the button **Déposer un fichier**. Select **Salaires et/ou honoraires** and follow the instructions.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/l10n-france/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/l10n-france/issues/new?body=module:%20l10n_fr_das2%0Aversion:%2014.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
~~~~~~~

* Akretion

Contributors
~~~~~~~~~~~~

* Alexis de Lattre <alexis.delattre@akretion.com>

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-alexis-via| image:: https://github.com/alexis-via.png?size=40px
:target: https://github.com/alexis-via
:alt: alexis-via

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

|maintainer-alexis-via|

This module is part of the `OCA/l10n-france <https://github.com/OCA/l10n-france/tree/14.0/l10n_fr_das2>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Will be generated from readme subdir
2 changes: 1 addition & 1 deletion l10n_fr_das2/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"l10n_fr_cog",
],
"external_dependencies": {
"python": ["unidecode", "stdnum", "pgpy"],
"python": ["stdnum", "pyfrdas2"],
},
"data": [
"security/das2_security.xml",
Expand Down
64 changes: 15 additions & 49 deletions l10n_fr_das2/models/l10n_fr_das2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import base64
import gzip
import logging
from datetime import datetime

from pyfrdas2 import format_street_block, generate_file
from stdnum.fr.siret import is_valid

from odoo import _, api, fields, models, tools
Expand All @@ -15,17 +15,6 @@

logger = logging.getLogger(__name__)

try:
from unidecode import unidecode
except ImportError:
unidecode = False
logger.debug("Cannot import unidecode")
try:
import pgpy
except ImportError:
pgpy = False
logger.debug("Cannot import pgpy")


FRANCE_CODES = (
"FR",
Expand Down Expand Up @@ -198,10 +187,10 @@ def done(self):
% encryption
)

attach = self.generate_file(encryption=encryption)
attach = self.generate_file_and_attach(encryption=encryption)
self.message_post(body=msg)
# also generate a clear file, for audit purposes
unencrypted_attach = self.generate_file(encryption="none")
unencrypted_attach = self.generate_file_and_attach(encryption="none")
vals.update(
{
"attachment_id": attach.id,
Expand Down Expand Up @@ -314,11 +303,8 @@ def _prepare_line(self, partner, base_domain):
field_name: amount_int,
"parent_id": self.id,
"partner_id": partner.id,
"job": partner.fr_das2_job,
"note": "<ul>%s</ul>" % note,
}
if partner.siren and partner.nic:
res["partner_siret"] = partner.siret
return res

def generate_warning_msg(self, das2_partners):
Expand Down Expand Up @@ -438,14 +424,14 @@ def _prepare_field(

def _prepare_address(self, partner):
cstreet2 = self._prepare_field("Street2", partner, partner.street2, 32)
cstreet = self._prepare_field("Street", partner, partner.street, 32)
# specs section 5.4 : only bureau distributeur and code postal are
cstreet = format_street_block(partner.street)
# specs : only bureau distributeur and code postal are
# required. And they say it is tolerated to set city as
# bureau distributeur
# And we don't set the commune field because we put the same info
# in bureau distributeur (section 5.4.1.3)
# specs section 5.4 and 5.4.1.2.b: it is possible to set the field
# "Adresse voie" without structuration on 32 chars => that's what we do
# in bureau distributeur
# specs 2024 : we now have to have structured adresses with a separation
# for the number, bis/ter and street name. We'll do our best to comply with that.
if not partner.city:
raise UserError(_("Missing city on partner '%s'.") % partner.display_name)
if partner.country_id and partner.country_id.code not in FRANCE_CODES:
Expand Down Expand Up @@ -696,7 +682,7 @@ def _prepare_file(self):
file_content = "\r\n".join(flines) + "\r\n"
return file_content

def generate_file(self, encryption="prod"):
def generate_file_and_attach(self, encryption="prod"):
self.ensure_one()
assert encryption in ("prod", "test", "none")
company = self.company_id
Expand Down Expand Up @@ -746,39 +732,19 @@ def generate_file(self, encryption="prod"):
)
) from e

if encryption in ("prod", "test"):
prefix = "DSAL_"
file_ext = "txt.gz.gpg"
key_path = (
"l10n_fr_das2/pgp_keys/DGFIP_TIERSDECLARANTS_%s.asc"
% encryption.upper()
try:
file_bytes_result, filename = generate_file(
file_content_encoded, self.year, company.siren, encryption=encryption
)
with tools.file_open(key_path, mode="rb") as key_file:
key_file_blob = key_file.read()
pubkey = pgpy.PGPKey.from_blob(key_file_blob)[0]
file_content_compressed = gzip.compress(file_content_encoded)
to_encrypt_object = pgpy.PGPMessage.new(file_content_compressed)
file_content_final = bytes(pubkey.encrypt(to_encrypt_object))
else:
file_content_final = file_content_encoded
prefix = "UNENCRYPTED_DAS2_for_audit-"
file_ext = "txt"

user_local_datetime = fields.Datetime.context_timestamp(self, datetime.now())
filename = "%(prefix)s%(year)s_%(siren)s_000_%(gentime)s.%(file_ext)s" % {
"prefix": prefix,
"year": self.year,
"siren": company.siren,
"gentime": user_local_datetime.strftime("%Y%m%d%H%M%S"),
"file_ext": file_ext,
}
except Exception as e:
raise UserError(e) from e

attach = self.env["ir.attachment"].create(
{
"name": filename,
"res_id": self.id,
"res_model": self._name,
"datas": base64.encodebytes(file_content_final),
"datas": base64.encodebytes(file_bytes_result),
}
)
return attach
Expand Down
49 changes: 0 additions & 49 deletions l10n_fr_das2/pgp_keys/DGFIP_TIERSDECLARANTS_PROD.asc

This file was deleted.

49 changes: 0 additions & 49 deletions l10n_fr_das2/pgp_keys/DGFIP_TIERSDECLARANTS_TEST.asc

This file was deleted.

7 changes: 7 additions & 0 deletions l10n_fr_das2/readme/INSTALL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This module requires the Python lib `pyfrdas2 <https://pypi.org/project/pyfrdas2/>`_. As this lib contains the PGP encryption keys of the DGFiP used to encrypt the declaration file and as these keys are changed every year, check that the lib is up-to-date before using the module for a new year.

To install this lib, run:

.. code::
pip3 install --upgrade pyfrdas2
Loading

0 comments on commit 450d8f0

Please sign in to comment.