From da10eded8c986d7d81aa8061669da5a088fefec1 Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Thu, 3 Mar 2016 09:07:17 +0100 Subject: [PATCH] [ADD] l10n_fr_fec: FEC export for the french accounting. Courtesy of Alexis de Lattre (Akretion) --- addons/l10n_fr_fec/README.rst | 53 +++++++ addons/l10n_fr_fec/__init__.py | 6 + addons/l10n_fr_fec/__openerp__.py | 20 +++ addons/l10n_fr_fec/i18n/fr.po | 127 +++++++++++++++ addons/l10n_fr_fec/i18n/l10n_fr_fec.pot | 127 +++++++++++++++ addons/l10n_fr_fec/wizard/__init__.py | 6 + addons/l10n_fr_fec/wizard/fec.py | 200 ++++++++++++++++++++++++ addons/l10n_fr_fec/wizard/fec_view.xml | 38 +++++ 8 files changed, 577 insertions(+) create mode 100644 addons/l10n_fr_fec/README.rst create mode 100644 addons/l10n_fr_fec/__init__.py create mode 100644 addons/l10n_fr_fec/__openerp__.py create mode 100644 addons/l10n_fr_fec/i18n/fr.po create mode 100644 addons/l10n_fr_fec/i18n/l10n_fr_fec.pot create mode 100644 addons/l10n_fr_fec/wizard/__init__.py create mode 100644 addons/l10n_fr_fec/wizard/fec.py create mode 100644 addons/l10n_fr_fec/wizard/fec_view.xml diff --git a/addons/l10n_fr_fec/README.rst b/addons/l10n_fr_fec/README.rst new file mode 100644 index 00000000000..509e576957c --- /dev/null +++ b/addons/l10n_fr_fec/README.rst @@ -0,0 +1,53 @@ +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 ` +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 ` 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 ` +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 > Legal +Reports > Journals > FEC* qui va démarrer l'assistant de génération du FEC. + +Credits +======= + +Contributors +------------ + +* Alexis de Lattre + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/addons/l10n_fr_fec/__init__.py b/addons/l10n_fr_fec/__init__.py new file mode 100644 index 00000000000..22a9da6d839 --- /dev/null +++ b/addons/l10n_fr_fec/__init__.py @@ -0,0 +1,6 @@ +# -*- encoding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +import wizard diff --git a/addons/l10n_fr_fec/__openerp__.py b/addons/l10n_fr_fec/__openerp__.py new file mode 100644 index 00000000000..389986a1cdc --- /dev/null +++ b/addons/l10n_fr_fec/__openerp__.py @@ -0,0 +1,20 @@ +# -*- encoding: 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', + 'version': '1.0', + 'category': 'French Localization', + 'license': 'AGPL-3', + 'summary': "Fichier d'Échange Informatisé (FEC) for France", + 'author': "Akretion,Odoo Community Association (OCA)", + 'website': 'http://www.akretion.com', + 'depends': ['l10n_fr', 'account_accountant'], + 'data': [ + 'wizard/fec_view.xml', + ], + 'installable': True, + 'auto_install': True, +} diff --git a/addons/l10n_fr_fec/i18n/fr.po b/addons/l10n_fr_fec/i18n/fr.po new file mode 100644 index 00000000000..9accbaeacd0 --- /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:27+0000\n" +"PO-Revision-Date: 2014-02-03 10:27+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 +#: 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 00000000000..2e7341bd595 --- /dev/null +++ b/addons/l10n_fr_fec/i18n/l10n_fr_fec.pot @@ -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:31+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 +#: view:account.fr.fec:0 +msgid "FEC File Generation" +msgstr "" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "The encoding of this text file is UTF-8." +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.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 "" + +#. 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 "" + +#. module: l10n_fr_fec +#: field:account.fr.fec,fec_data:0 +msgid "FEC File" +msgstr "" + +#. module: l10n_fr_fec +#: field:account.fr.fec,fiscalyear_id:0 +msgid "Fiscal Year" +msgstr "" + +#. module: l10n_fr_fec +#: field:account.fr.fec,type:0 +msgid "Company Type" +msgstr "" + +#. 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 "" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_fr_fec +#: view:account.fr.fec:0 +msgid "Close" +msgstr "" + +#. 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 "" + +#. module: l10n_fr_fec +#: code:addons/l10n_fr_fec/wizard/fec.py:180 +#, python-format +msgid "FEC is for French companies only !" +msgstr "" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,export_type:0 +msgid "Official FEC report (posted entries only)" +msgstr "" + +#. module: l10n_fr_fec +#: selection:account.fr.fec,export_type:0 +msgid "Non-official FEC report (posted and unposted entries)" +msgstr "" + +#. module: l10n_fr_fec +#: field:account.fr.fec,export_type:0 +msgid "Export Type" +msgstr "" + diff --git a/addons/l10n_fr_fec/wizard/__init__.py b/addons/l10n_fr_fec/wizard/__init__.py new file mode 100644 index 00000000000..7a60fc6dc5a --- /dev/null +++ b/addons/l10n_fr_fec/wizard/__init__.py @@ -0,0 +1,6 @@ +# -*- encoding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +import fec diff --git a/addons/l10n_fr_fec/wizard/fec.py b/addons/l10n_fr_fec/wizard/fec.py new file mode 100644 index 00000000000..5aa6b177415 --- /dev/null +++ b/addons/l10n_fr_fec/wizard/fec.py @@ -0,0 +1,200 @@ +# -*- encoding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Copyright (C) 2013-2015 Akretion (http://www.akretion.com) + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning +import base64 +import StringIO +import csv + + +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) + filename = fields.Char(string='Filename', size=256, readonly=True) + 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.multi + 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 + header = [ + 'JournalCode', # 0 + 'JournalLib', # 1 + 'EcritureNum', # 2 + 'EcritureDate', # 3 + 'CompteNum', # 4 + 'CompteLib', # 5 + 'CompAuxNum', # 6 We use partner.id + 'CompAuxLib', # 7 + 'PieceRef', # 8 + 'PieceDate', # 9 + 'EcritureLib', # 10 + 'Debit', # 11 + 'Credit', # 12 + 'EcritureLet', # 13 + 'DateLet', # 14 + 'ValidDate', # 15 + 'Montantdevise', # 16 + 'Idevise', # 17 + ] + + company = self.env.user.company_id + if not company.vat: + raise Warning( + _("Missing VAT number for company %s") % company.name) + if company.vat[0:2] != 'FR': + raise Warning( + _("FEC is for French companies only !")) + + fecfile = StringIO.StringIO() + w = csv.writer(fecfile, delimiter='|') + w.writerow(header) + + # INITIAL BALANCE + sql_query = ''' + SELECT + 'OUV' AS JournalCode, + 'Balance initiale' AS JournalLib, + 'Balance initiale ' || MIN(aa.name) AS EcritureNum, + %s AS EcritureDate, + MIN(aa.code) AS CompteNum, + replace(MIN(aa.name), '|', '/') 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), '999999999999999D99') END, '.', ',') AS Debit, + replace(CASE WHEN sum(aml.balance) >= 0 THEN '0,00' ELSE to_char(-SUM(aml.balance), '999999999999999D99') 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 + WHERE + 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 += ''' + GROUP BY aml.account_id + ''' + formatted_date_from = self.date_from.replace('-', '') + self._cr.execute( + sql_query, (formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id)) + + for row in self._cr.fetchall(): + listrow = list(row) + w.writerow([s.encode("utf-8") for s in listrow]) + + # LINES + sql_query = ''' + SELECT + replace(aj.code, '|', '/') AS JournalCode, + replace(aj.name, '|', '/') AS JournalLib, + replace(am.name, '|', '/') AS EcritureNum, + TO_CHAR(am.date, 'YYYYMMDD') AS EcritureDate, + aa.code AS CompteNum, + replace(aa.name, '|', '/') AS CompteLib, + CASE WHEN rp.ref IS null OR rp.ref = '' + THEN 'ID ' || rp.id + ELSE rp.ref + END + AS CompAuxNum, + replace(rp.name, '|', '/') AS CompAuxLib, + CASE WHEN am.ref IS null OR am.ref = '' + THEN '-' + ELSE replace(am.ref, '|', '/') + END + AS PieceRef, + TO_CHAR(am.date, 'YYYYMMDD') AS PieceDate, + CASE WHEN aml.name IS NULL THEN '/' ELSE replace(aml.name, '|', '/') END AS EcritureLib, + replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '999999999999999D99') END, '.', ',') AS Debit, + replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '999999999999999D99') 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, + replace(CASE WHEN aml.amount_currency = 0 THEN '' ELSE to_char(aml.amount_currency, '999999999999999D99') 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 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(): + listrow = list(row) + w.writerow([s.encode("utf-8") for s in listrow]) + + siren = company.vat[4:13] + end_date = self.date_to.replace('-', '') + suffix = '' + if self.export_type == "nonofficial": + suffix = '-NONOFFICIAL' + fecvalue = fecfile.getvalue() + self.write({ + 'fec_data': base64.encodestring(fecvalue), + # Filename = FECYYYYMMDD where YYYMMDD is the closing date + 'filename': '%sFEC%s%s.csv' % (siren, end_date, suffix), + }) + fecfile.close() + + action = { + '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', + } + return action diff --git a/addons/l10n_fr_fec/wizard/fec_view.xml b/addons/l10n_fr_fec/wizard/fec_view.xml new file mode 100644 index 00000000000..e9ea5a40dbe --- /dev/null +++ b/addons/l10n_fr_fec/wizard/fec_view.xml @@ -0,0 +1,38 @@ + + + + + + account.fr.fec.form.view + account.fr.fec + +
+ + + + + +
+
+
+
+
+ + + FEC + account.fr.fec + form + new + + + + +
+