diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/l10n_fr_fec | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/l10n_fr_fec')
| -rw-r--r-- | addons/l10n_fr_fec/README.rst | 40 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/__init__.py | 6 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/__manifest__.py | 18 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/i18n/fr.po | 127 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/i18n/l10n_fr_fec.pot | 291 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/wizard/__init__.py | 6 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/wizard/account_fr_fec.py | 425 | ||||
| -rw-r--r-- | addons/l10n_fr_fec/wizard/account_fr_fec_view.xml | 136 |
9 files changed, 1051 insertions, 0 deletions
diff --git a/addons/l10n_fr_fec/README.rst b/addons/l10n_fr_fec/README.rst new file mode 100644 index 00000000..cbd9da1d --- /dev/null +++ b/addons/l10n_fr_fec/README.rst @@ -0,0 +1,40 @@ +Fichier d'Échange Informatisé (FEC) pour la France +================================================== + +Ce module permet de générer le fichier FEC tel que définit par `l'arrêté du 29 +Juillet 2013 <http://legifrance.gouv.fr/eli/arrete/2013/7/29/BUDE1315492A/jo/texte>` +portant modification des dispositions de l'article A. 47 A-1 du +livre des procédures fiscales. + +Cet arrêté prévoit l'obligation pour les sociétés ayant une comptabilité +informatisée de pouvoir fournir à l'administration fiscale un fichier +regroupant l'ensemble des écritures comptables de l'exercice. Le format de ce +fichier, appelé *FEC*, est définit dans l'arrêté. + +Le détail du format du FEC est spécifié dans le bulletin officiel des finances publiques `BOI-CF-IOR-60-40-20-20131213 <http://bofip.impots.gouv.fr/bofip/ext/pdf/createPdfWithAnnexePermalien/BOI-CF-IOR-60-40-20-20131213.pdf?doc=9028-PGP&identifiant=BOI-CF-IOR-60-40-20-20131213>` du 13 Décembre 2013. Ce module implémente le fichier +FEC au format texte et non au format XML, car le format texte sera facilement +lisible et vérifiable par le comptable en utilisant un tableur. + +La structure du fichier FEC généré par ce module a été vérifiée avec le logiciel +*Test Compta Demat* version 1_00_05 disponible sur +`le site de la direction générale des finances publiques <http://www.economie.gouv.fr/dgfip/outil-test-des-fichiers-des-ecritures-comptables-fec>` +en utilisant une base de donnée Odoo réelle. + +Configuration +============= + +Aucune configuration n'est nécessaire. + +Utilisation +=========== + +Pour générer le *FEC*, allez dans le menu *Accounting > Reporting > French Statements > FEC* qui va démarrer l'assistant de génération du FEC. + +Credits +======= + +Contributors +------------ + +* Alexis de Lattre <alexis.delattre@akretion.com> + diff --git a/addons/l10n_fr_fec/__init__.py b/addons/l10n_fr_fec/__init__.py new file mode 100644 index 00000000..58d23596 --- /dev/null +++ b/addons/l10n_fr_fec/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +from . import wizard diff --git a/addons/l10n_fr_fec/__manifest__.py b/addons/l10n_fr_fec/__manifest__.py new file mode 100644 index 00000000..ef1d3043 --- /dev/null +++ b/addons/l10n_fr_fec/__manifest__.py @@ -0,0 +1,18 @@ +#-*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +{ + 'name': 'France - FEC', + 'category': 'Accounting/Localizations/Reporting', + 'summary': "Fichier d'Échange Informatisé (FEC) for France", + 'author': "Akretion,Odoo Community Association (OCA)", + 'depends': ['l10n_fr', 'account'], + 'data': [ + 'security/ir.model.access.csv', + 'wizard/account_fr_fec_view.xml', + ], + 'auto_install': True, + 'license': 'LGPL-3', +} diff --git a/addons/l10n_fr_fec/i18n/fr.po b/addons/l10n_fr_fec/i18n/fr.po new file mode 100644 index 00000000..83e1e72a --- /dev/null +++ b/addons/l10n_fr_fec/i18n/fr.po @@ -0,0 +1,127 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_fr_fec +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-03 10:31+0000\n" +"PO-Revision-Date: 2014-02-03 10:27+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "FEC File Generation" +msgstr "Génération Fichier FEC" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "The encoding of this text file is UTF-8." +msgstr "Ce fichier texte est encodé en UTF-8." + +#. module: l10n_fr_fec +#: model:ir.model,name:l10n_fr_fec.model_account_fr_fec +msgid "Ficher Echange Informatise" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.actions.act_window,name:l10n_fr_fec.account_fr_fec_action +#: model:ir.ui.menu,name:l10n_fr_fec.account_fr_fec_menu +msgid "FEC" +msgstr "" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/fec.py:175 +#: code:addons/l10n_fr_fec/wizard/fec.py:179 +#, python-format +msgid "Error:" +msgstr "Erreur :" + +#. module: l10n_fr_fec +#: field:account.fr.fec,state:0 +msgid "State" +msgstr "" + +#. module: l10n_fr_fec +#: field:account.fr.fec,filename:0 +msgid "Filename" +msgstr "Nom du fichier" + +#. module: l10n_fr_fec +#: field:account.fr.fec,fec_data:0 +msgid "FEC File" +msgstr "Fichier FEC" + +#. module: l10n_fr_fec +#: field:account.fr.fec,fiscalyear_id:0 +msgid "Fiscal Year" +msgstr "Année fiscale" + +#. module: l10n_fr_fec +#: field:account.fr.fec,type:0 +msgid "Company Type" +msgstr "Type de société" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,state:0 +msgid "Done" +msgstr "" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/fec.py:176 +#, python-format +msgid "Missing VAT number for company %s" +msgstr "Numéro de TVA manquant sur la société %s" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "Cancel" +msgstr "Annuler" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "Close" +msgstr "Fermer" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,type:0 +msgid "I.S. ou I.R. aux BIC" +msgstr "" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "Generate" +msgstr "Générer" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/fec.py:180 +#, python-format +msgid "FEC is for French companies only !" +msgstr "Le FEC est uniquement pour les sociétés françaises !" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,export_type:0 +msgid "Official FEC report (posted entries only)" +msgstr "Rapport FEC officiel (entrées postées uniquement)" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,export_type:0 +msgid "Non-official FEC report (posted and unposted entries)" +msgstr "Rapport FEC non-officiel (entrées postées et non-postées)" + +#. module: l10n_fr_fec +#: field:account.fr.fec,export_type:0 +msgid "Export Type" +msgstr "Type d'export" diff --git a/addons/l10n_fr_fec/i18n/l10n_fr_fec.pot b/addons/l10n_fr_fec/i18n/l10n_fr_fec.pot new file mode 100644 index 00000000..073ccc52 --- /dev/null +++ b/addons/l10n_fr_fec/i18n/l10n_fr_fec.pot @@ -0,0 +1,291 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_fr_fec +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-01-29 15:06+0000\n" +"PO-Revision-Date: 2020-01-29 15:06+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 10" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 11" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 12" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 13" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 14" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 15" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 16" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "# 17" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Cancel" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Column" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Comment" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "CompAuxLib" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "CompAuxNum" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "CompteLib" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "CompteNum" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__create_uid +msgid "Created by" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__create_date +msgid "Created on" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Credit" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "DateLet" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Debit" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "EcritureDate" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "EcritureLet" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "EcritureLib" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "EcritureNum" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__date_to +msgid "End Date" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__export_type +msgid "Export Type" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.actions.act_window,name:l10n_fr_fec.account_fr_fec_action +#: model:ir.ui.menu,name:l10n_fr_fec.account_fr_fec_menu +msgid "FEC" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__fec_data +msgid "FEC File" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "FEC File Generation" +msgstr "" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/account_fr_fec.py:0 +#, python-format +msgid "FEC is for French companies only !" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model,name:l10n_fr_fec.model_account_fr_fec +msgid "Ficher Echange Informatise" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__filename +msgid "Filename" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Generate" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__id +msgid "ID" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Idevise" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "JournalCode" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "JournalLib" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__write_date +msgid "Last Updated on" +msgstr "" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/account_fr_fec.py:0 +#, python-format +msgid "Missing VAT number for company %s" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Montantdevise" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields.selection,name:l10n_fr_fec.selection__account_fr_fec__export_type__nonofficial +msgid "Non-official FEC report (posted and unposted entries)" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields.selection,name:l10n_fr_fec.selection__account_fr_fec__export_type__official +msgid "Official FEC report (posted entries only)" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Options" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "PieceDate" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "PieceRef" +msgstr "" + +#. module: l10n_fr_fec +#: model:ir.model.fields,field_description:l10n_fr_fec.field_account_fr_fec__date_from +msgid "Start Date" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Technical Info" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "Technical Name" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "" +"The encoding of this text file is UTF-8. The structure of file is CSV " +"separated by pipe '|'." +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "ValidDate" +msgstr "" + +#. module: l10n_fr_fec +#: model_terms:ir.ui.view,arch_db:l10n_fr_fec.account_fr_fec_view +msgid "We use partner.id" +msgstr "" diff --git a/addons/l10n_fr_fec/security/ir.model.access.csv b/addons/l10n_fr_fec/security/ir.model.access.csv new file mode 100644 index 00000000..430b92b3 --- /dev/null +++ b/addons/l10n_fr_fec/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_fr_fec,account.fr.fec,model_account_fr_fec,account.group_account_user,1,1,1,0 diff --git a/addons/l10n_fr_fec/wizard/__init__.py b/addons/l10n_fr_fec/wizard/__init__.py new file mode 100644 index 00000000..e16a3c15 --- /dev/null +++ b/addons/l10n_fr_fec/wizard/__init__.py @@ -0,0 +1,6 @@ +#-*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +from . import account_fr_fec diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py new file mode 100644 index 00000000..d80c07d5 --- /dev/null +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -0,0 +1,425 @@ +#-*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +import base64 +import io + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError +from odoo.tools import float_is_zero, pycompat + + +class AccountFrFec(models.TransientModel): + _name = 'account.fr.fec' + _description = 'Ficher Echange Informatise' + + date_from = fields.Date(string='Start Date', required=True) + date_to = fields.Date(string='End Date', required=True) + fec_data = fields.Binary('FEC File', readonly=True, attachment=False) + filename = fields.Char(string='Filename', size=256, readonly=True) + test_file = fields.Boolean() + export_type = fields.Selection([ + ('official', 'Official FEC report (posted entries only)'), + ('nonofficial', 'Non-official FEC report (posted and unposted entries)'), + ], string='Export Type', required=True, default='official') + + @api.onchange('test_file') + def _onchange_export_file(self): + if not self.test_file: + self.export_type = 'official' + + def do_query_unaffected_earnings(self): + ''' Compute the sum of ending balances for all accounts that are of a type that does not bring forward the balance in new fiscal years. + This is needed because we have to display only one line for the initial balance of all expense/revenue accounts in the FEC. + ''' + + sql_query = ''' + SELECT + 'OUV' AS JournalCode, + 'Balance initiale' AS JournalLib, + 'OUVERTURE/' || %s AS EcritureNum, + %s AS EcritureDate, + '120/129' AS CompteNum, + 'Benefice (perte) reporte(e)' AS CompteLib, + '' AS CompAuxNum, + '' AS CompAuxLib, + '-' AS PieceRef, + %s AS PieceDate, + '/' AS EcritureLib, + replace(CASE WHEN COALESCE(sum(aml.balance), 0) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, + replace(CASE WHEN COALESCE(sum(aml.balance), 0) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, + '' AS EcritureLet, + '' AS DateLet, + %s AS ValidDate, + '' AS Montantdevise, + '' AS Idevise + FROM + account_move_line aml + LEFT JOIN account_move am ON am.id=aml.move_id + JOIN account_account aa ON aa.id = aml.account_id + LEFT JOIN account_account_type aat ON aa.user_type_id = aat.id + WHERE + am.date < %s + AND am.company_id = %s + AND aat.include_initial_balance IS NOT TRUE + AND (aml.debit != 0 OR aml.credit != 0) + ''' + # For official report: only use posted entries + if self.export_type == "official": + sql_query += ''' + AND am.state = 'posted' + ''' + company = self.env.company + formatted_date_from = fields.Date.to_string(self.date_from).replace('-', '') + date_from = self.date_from + formatted_date_year = date_from.year + self._cr.execute( + sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) + listrow = [] + row = self._cr.fetchone() + listrow = list(row) + return listrow + + def _get_company_legal_data(self, company): + """ + Dom-Tom are excluded from the EU's fiscal territory + Those regions do not have SIREN + sources: + https://www.service-public.fr/professionnels-entreprises/vosdroits/F23570 + http://www.douane.gouv.fr/articles/a11024-tva-dans-les-dom + """ + dom_tom_group = self.env.ref('l10n_fr.dom-tom') + is_dom_tom = company.country_id.code in dom_tom_group.country_ids.mapped('code') + if not is_dom_tom and not company.vat: + raise UserError(_("Missing VAT number for company %s", company.name)) + if not is_dom_tom and company.vat[0:2] != 'FR': + raise UserError(_("FEC is for French companies only !")) + + return { + 'siren': company.vat[4:13] if not is_dom_tom else '', + } + + def generate_fec(self): + self.ensure_one() + # We choose to implement the flat file instead of the XML + # file for 2 reasons : + # 1) the XSD file impose to have the label on the account.move + # but Odoo has the label on the account.move.line, so that's a + # problem ! + # 2) CSV files are easier to read/use for a regular accountant. + # So it will be easier for the accountant to check the file before + # sending it to the fiscal administration + today = fields.Date.today() + if self.date_from > today or self.date_to > today: + raise UserError(_('You could not set the start date or the end date in the future.')) + if self.date_from >= self.date_to: + raise UserError(_('The start date must be inferior to the end date.')) + + company = self.env.company + company_legal_data = self._get_company_legal_data(company) + + header = [ + u'JournalCode', # 0 + u'JournalLib', # 1 + u'EcritureNum', # 2 + u'EcritureDate', # 3 + u'CompteNum', # 4 + u'CompteLib', # 5 + u'CompAuxNum', # 6 We use partner.id + u'CompAuxLib', # 7 + u'PieceRef', # 8 + u'PieceDate', # 9 + u'EcritureLib', # 10 + u'Debit', # 11 + u'Credit', # 12 + u'EcritureLet', # 13 + u'DateLet', # 14 + u'ValidDate', # 15 + u'Montantdevise', # 16 + u'Idevise', # 17 + ] + + rows_to_write = [header] + # INITIAL BALANCE + unaffected_earnings_xml_ref = self.env.ref('account.data_unaffected_earnings') + unaffected_earnings_line = True # used to make sure that we add the unaffected earning initial balance only once + if unaffected_earnings_xml_ref: + #compute the benefit/loss of last year to add in the initial balance of the current year earnings account + unaffected_earnings_results = self.do_query_unaffected_earnings() + unaffected_earnings_line = False + + sql_query = ''' + SELECT + 'OUV' AS JournalCode, + 'Balance initiale' AS JournalLib, + 'OUVERTURE/' || %s AS EcritureNum, + %s AS EcritureDate, + MIN(aa.code) AS CompteNum, + replace(replace(MIN(aa.name), '|', '/'), '\t', '') AS CompteLib, + '' AS CompAuxNum, + '' AS CompAuxLib, + '-' AS PieceRef, + %s AS PieceDate, + '/' AS EcritureLib, + replace(CASE WHEN sum(aml.balance) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, + replace(CASE WHEN sum(aml.balance) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, + '' AS EcritureLet, + '' AS DateLet, + %s AS ValidDate, + '' AS Montantdevise, + '' AS Idevise, + MIN(aa.id) AS CompteID + FROM + account_move_line aml + LEFT JOIN account_move am ON am.id=aml.move_id + JOIN account_account aa ON aa.id = aml.account_id + LEFT JOIN account_account_type aat ON aa.user_type_id = aat.id + WHERE + am.date < %s + AND am.company_id = %s + AND aat.include_initial_balance = 't' + AND (aml.debit != 0 OR aml.credit != 0) + ''' + + # For official report: only use posted entries + if self.export_type == "official": + sql_query += ''' + AND am.state = 'posted' + ''' + + sql_query += ''' + GROUP BY aml.account_id, aat.type + HAVING round(sum(aml.balance), %s) != 0 + AND aat.type not in ('receivable', 'payable') + ''' + formatted_date_from = fields.Date.to_string(self.date_from).replace('-', '') + date_from = self.date_from + formatted_date_year = date_from.year + currency_digits = 2 + + self._cr.execute( + sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id, currency_digits)) + + for row in self._cr.fetchall(): + listrow = list(row) + account_id = listrow.pop() + if not unaffected_earnings_line: + account = self.env['account.account'].browse(account_id) + if account.user_type_id.id == self.env.ref('account.data_unaffected_earnings').id: + #add the benefit/loss of previous fiscal year to the first unaffected earnings account found. + unaffected_earnings_line = True + current_amount = float(listrow[11].replace(',', '.')) - float(listrow[12].replace(',', '.')) + unaffected_earnings_amount = float(unaffected_earnings_results[11].replace(',', '.')) - float(unaffected_earnings_results[12].replace(',', '.')) + listrow_amount = current_amount + unaffected_earnings_amount + if float_is_zero(listrow_amount, precision_digits=currency_digits): + continue + if listrow_amount > 0: + listrow[11] = str(listrow_amount).replace('.', ',') + listrow[12] = '0,00' + else: + listrow[11] = '0,00' + listrow[12] = str(-listrow_amount).replace('.', ',') + rows_to_write.append(listrow) + + #if the unaffected earnings account wasn't in the selection yet: add it manually + if (not unaffected_earnings_line + and unaffected_earnings_results + and (unaffected_earnings_results[11] != '0,00' + or unaffected_earnings_results[12] != '0,00')): + #search an unaffected earnings account + unaffected_earnings_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_unaffected_earnings').id)], limit=1) + if unaffected_earnings_account: + unaffected_earnings_results[4] = unaffected_earnings_account.code + unaffected_earnings_results[5] = unaffected_earnings_account.name + rows_to_write.append(unaffected_earnings_results) + + # INITIAL BALANCE - receivable/payable + sql_query = ''' + SELECT + 'OUV' AS JournalCode, + 'Balance initiale' AS JournalLib, + 'OUVERTURE/' || %s AS EcritureNum, + %s AS EcritureDate, + MIN(aa.code) AS CompteNum, + replace(MIN(aa.name), '|', '/') AS CompteLib, + CASE WHEN MIN(aat.type) IN ('receivable', 'payable') + THEN + CASE WHEN rp.ref IS null OR rp.ref = '' + THEN rp.id::text + ELSE replace(rp.ref, '|', '/') + END + ELSE '' + END + AS CompAuxNum, + CASE WHEN aat.type IN ('receivable', 'payable') + THEN COALESCE(replace(rp.name, '|', '/'), '') + ELSE '' + END AS CompAuxLib, + '-' AS PieceRef, + %s AS PieceDate, + '/' AS EcritureLib, + replace(CASE WHEN sum(aml.balance) <= 0 THEN '0,00' ELSE to_char(SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Debit, + replace(CASE WHEN sum(aml.balance) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '000000000000000D99') END, '.', ',') AS Credit, + '' AS EcritureLet, + '' AS DateLet, + %s AS ValidDate, + '' AS Montantdevise, + '' AS Idevise, + MIN(aa.id) AS CompteID + FROM + account_move_line aml + LEFT JOIN account_move am ON am.id=aml.move_id + LEFT JOIN res_partner rp ON rp.id=aml.partner_id + JOIN account_account aa ON aa.id = aml.account_id + LEFT JOIN account_account_type aat ON aa.user_type_id = aat.id + WHERE + am.date < %s + AND am.company_id = %s + AND aat.include_initial_balance = 't' + AND (aml.debit != 0 OR aml.credit != 0) + ''' + + # For official report: only use posted entries + if self.export_type == "official": + sql_query += ''' + AND am.state = 'posted' + ''' + + sql_query += ''' + GROUP BY aml.account_id, aat.type, rp.ref, rp.id + HAVING round(sum(aml.balance), %s) != 0 + AND aat.type in ('receivable', 'payable') + ''' + self._cr.execute( + sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id, currency_digits)) + + for row in self._cr.fetchall(): + listrow = list(row) + account_id = listrow.pop() + rows_to_write.append(listrow) + + # LINES + sql_query = ''' + SELECT + replace(replace(aj.code, '|', '/'), '\t', '') AS JournalCode, + replace(replace(aj.name, '|', '/'), '\t', '') AS JournalLib, + replace(replace(am.name, '|', '/'), '\t', '') AS EcritureNum, + TO_CHAR(am.date, 'YYYYMMDD') AS EcritureDate, + aa.code AS CompteNum, + replace(replace(aa.name, '|', '/'), '\t', '') AS CompteLib, + CASE WHEN aat.type IN ('receivable', 'payable') + THEN + CASE WHEN rp.ref IS null OR rp.ref = '' + THEN rp.id::text + ELSE replace(rp.ref, '|', '/') + END + ELSE '' + END + AS CompAuxNum, + CASE WHEN aat.type IN ('receivable', 'payable') + THEN COALESCE(replace(replace(rp.name, '|', '/'), '\t', ''), '') + ELSE '' + END AS CompAuxLib, + CASE WHEN am.ref IS null OR am.ref = '' + THEN '-' + ELSE replace(replace(am.ref, '|', '/'), '\t', '') + END + AS PieceRef, + TO_CHAR(am.date, 'YYYYMMDD') AS PieceDate, + CASE WHEN aml.name IS NULL OR aml.name = '' THEN '/' + WHEN aml.name SIMILAR TO '[\t|\s|\n]*' THEN '/' + ELSE replace(replace(replace(replace(aml.name, '|', '/'), '\t', ''), '\n', ''), '\r', '') END AS EcritureLib, + replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '000000000000000D99') END, '.', ',') AS Debit, + replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '000000000000000D99') END, '.', ',') AS Credit, + CASE WHEN rec.name IS NULL THEN '' ELSE rec.name END AS EcritureLet, + CASE WHEN aml.full_reconcile_id IS NULL THEN '' ELSE TO_CHAR(rec.create_date, 'YYYYMMDD') END AS DateLet, + TO_CHAR(am.date, 'YYYYMMDD') AS ValidDate, + CASE + WHEN aml.amount_currency IS NULL OR aml.amount_currency = 0 THEN '' + ELSE replace(to_char(aml.amount_currency, '000000000000000D99'), '.', ',') + END AS Montantdevise, + CASE WHEN aml.currency_id IS NULL THEN '' ELSE rc.name END AS Idevise + FROM + account_move_line aml + LEFT JOIN account_move am ON am.id=aml.move_id + LEFT JOIN res_partner rp ON rp.id=aml.partner_id + JOIN account_journal aj ON aj.id = am.journal_id + JOIN account_account aa ON aa.id = aml.account_id + LEFT JOIN account_account_type aat ON aa.user_type_id = aat.id + LEFT JOIN res_currency rc ON rc.id = aml.currency_id + LEFT JOIN account_full_reconcile rec ON rec.id = aml.full_reconcile_id + WHERE + am.date >= %s + AND am.date <= %s + AND am.company_id = %s + AND (aml.debit != 0 OR aml.credit != 0) + ''' + + # For official report: only use posted entries + if self.export_type == "official": + sql_query += ''' + AND am.state = 'posted' + ''' + + sql_query += ''' + ORDER BY + am.date, + am.name, + aml.id + ''' + self._cr.execute( + sql_query, (self.date_from, self.date_to, company.id)) + + for row in self._cr.fetchall(): + rows_to_write.append(list(row)) + + fecvalue = self._csv_write_rows(rows_to_write) + end_date = fields.Date.to_string(self.date_to).replace('-', '') + suffix = '' + if self.export_type == "nonofficial": + suffix = '-NONOFFICIAL' + + self.write({ + 'fec_data': base64.encodebytes(fecvalue), + # Filename = <siren>FECYYYYMMDD where YYYMMDD is the closing date + 'filename': '%sFEC%s%s.csv' % (company_legal_data['siren'], end_date, suffix), + }) + + # Set fiscal year lock date to the end date (not in test) + fiscalyear_lock_date = self.env.company.fiscalyear_lock_date + if not self.test_file and (not fiscalyear_lock_date or fiscalyear_lock_date < self.date_to): + self.env.company.write({'fiscalyear_lock_date': self.date_to}) + return { + 'name': 'FEC', + 'type': 'ir.actions.act_url', + 'url': "web/content/?model=account.fr.fec&id=" + str(self.id) + "&filename_field=filename&field=fec_data&download=true&filename=" + self.filename, + 'target': 'self', + } + + def _csv_write_rows(self, rows, lineterminator=u'\r\n'): + """ + Write FEC rows into a file + It seems that Bercy's bureaucracy is not too happy about the + empty new line at the End Of File. + + @param {list(list)} rows: the list of rows. Each row is a list of strings + @param {unicode string} [optional] lineterminator: effective line terminator + Has nothing to do with the csv writer parameter + The last line written won't be terminated with it + + @return the value of the file + """ + fecfile = io.BytesIO() + writer = pycompat.csv_writer(fecfile, delimiter='|', lineterminator='') + + rows_length = len(rows) + for i, row in enumerate(rows): + if not i == rows_length - 1: + row[-1] += lineterminator + writer.writerow(row) + + fecvalue = fecfile.getvalue() + fecfile.close() + return fecvalue diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml b/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml new file mode 100644 index 00000000..2f7a66dc --- /dev/null +++ b/addons/l10n_fr_fec/wizard/account_fr_fec_view.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> +<record id="account_fr_fec_view" model="ir.ui.view"> + <field name="name">account.fr.fec.form.view</field> + <field name="model">account.fr.fec</field> + <field name="arch" type="xml"> + <form string="FEC File Generation"> + <div class="alert alert-info" role="alert" attrs="{'invisible': [('test_file', '=', True)]}"> + When you download a FEC file, the lock date is set to the end date. + If you want to test the FEC file generation, please tick the test file checkbox. + </div> + <div class="alert alert-info" role="alert" attrs="{'invisible': [('test_file', '=', False)]}"> + You are in test mode. The FEC file generation will not set the lock date. + </div> + <notebook> + <page string="Options" name="options"> + <group> + <field name="date_from"/> + <field name="date_to"/> + <field name="test_file"/> + <field name="export_type" attrs="{'invisible': [('test_file', '=', False)]}"/> + </group> + </page> + <page string="Technical Info" name="technical_info"> + <group> + <div> + The encoding of this text file is UTF-8. The structure of file is CSV separated by pipe '|'. + </div> + </group> + <group> + <table style="width:80%"> + <tr> + <th>Technical Name</th> + <th>Column</th> + <th>Comment</th> + </tr> + <tr> + <td>JournalCode</td> + <td># 0</td> + </tr> + <tr> + <td>JournalLib</td> + <td> + # 1</td> + </tr> + <tr> + <td>EcritureNum</td> + <td># 2</td> + </tr> + <tr> + <td>EcritureDate</td> + <td> + # 3</td> + </tr> + <tr> + <td>CompteNum</td> + <td># 4</td> + </tr> + <tr> + <td>CompteLib</td> + <td># 5</td> + </tr> + <tr> + <td>CompAuxNum</td> + <td># 6</td> + <td>We use partner.id</td> + </tr> + <tr> + <td>CompAuxLib</td> + <td># 7</td> + </tr> + <tr> + <td>PieceRef</td> + <td># 8</td> + </tr> + <tr> + <td>PieceDate</td> + <td># 9</td> + </tr> + <tr> + <td>EcritureLib</td> + <td># 10</td> + </tr> + <tr> + <td>Debit</td> + <td># 11</td> + </tr> + <tr> + <td>Credit</td> + <td># 12</td> + </tr> + <tr> + <td>EcritureLet</td> + <td># 13</td> + </tr> + <tr> + <td>DateLet</td> + <td># 14</td> + </tr> + <tr> + <td>ValidDate</td> + <td># 15</td> + </tr> + <tr> + <td>Montantdevise</td> + <td># 16</td> + </tr> + <tr> + <td>Idevise</td> + <td># 17</td> + </tr> + </table> + </group> + </page> + </notebook> + <footer> + <button string="Generate" name="generate_fec" type="object" + class="oe_highlight"/> + <button string="Cancel" class="btn btn-secondary" special="cancel"/> + </footer> + </form> + </field> +</record> + +<record id="account_fr_fec_action" model="ir.actions.act_window"> + <field name="name">FEC</field> + <field name="res_model">account.fr.fec</field> + <field name="view_mode">form</field> + <field name="target">new</field> +</record> + +<menuitem id="account_fr_fec_menu" + parent="l10n_fr.account_reports_fr_statements_menu" + action="account_fr_fec_action" + sequence="100" /> +</odoo> |
