[ADD] l10n_fr_fec: FEC export for the french accounting. Courtesy of Alexis de Lattre (Akretion)

This commit is contained in:
qdp-odoo
2016-03-03 09:07:17 +01:00
parent 66c1d18579
commit da10eded8c
8 changed files with 577 additions and 0 deletions
+53
View File
@@ -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 <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 > Legal
Reports > Journals > FEC* qui va démarrer l'assistant de génération du FEC.
Credits
=======
Contributors
------------
* Alexis de Lattre <alexis.delattre@akretion.com>
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.
+6
View File
@@ -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
+20
View File
@@ -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,
}
+127
View File
@@ -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"
+127
View File
@@ -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 ""
+6
View File
@@ -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
+200
View File
@@ -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 = <siren>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
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<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">
<group>
<field name="date_from"/>
<field name="date_to"/>
<field name="export_type" groups="base.group_no_one"/>
</group>
<footer>
<button string="Generate" name="generate_fec" type="object"
class="oe_highlight"/>
<button string="Cancel" special="cancel"
class="oe_link"/>
</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" />
</data>
</openerp>