diff --git a/addons/l10n_fr_certification/__init__.py b/addons/l10n_fr_certification/__init__.py
deleted file mode 100644
index dcd82374562..00000000000
--- a/addons/l10n_fr_certification/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import models
-from odoo import api, SUPERUSER_ID
-
-def _setup_inalterability(cr, registry):
- env = api.Environment(cr, SUPERUSER_ID, {})
- # enable ping for this module
- env['publisher_warranty.contract'].update_notification(cron_mode=True)
-
- # make sure account_cancel is not usable at the same time as l10n_fr
- # FORWARD PORT NOTICE
- # In master as of March 2017, RCO-ODOO coded an exclusive field on modules to flag incompatibility
-
- wanted_states = ['installed', 'to upgrade', 'to install']
- account_cancel_module = env['ir.module.module'].search([('name', '=', 'account_cancel')], limit=1)
-
- if account_cancel_module and account_cancel_module.state in wanted_states:
- views_xml_id = env['ir.model.data'].search([('module', '=', 'account_cancel'), ('model', '=', 'ir.ui.view')])
- ir_views = env['ir.ui.view'].browse([v.res_id for v in views_xml_id])
- for cancel_view in ir_views:
- cancel_view.write({'active': False})
-
- fr_companies = env['res.company'].search([('partner_id.country_id.code', '=', 'FR')])
- if fr_companies:
- # create the securisation sequence per company
- fr_companies._create_secure_sequence()
-
- #reset the update_posted field on journals
- journals = env['account.journal'].search([('company_id', 'in', fr_companies.ids)])
- for journal in journals:
- journal.write({'update_posted': False})
diff --git a/addons/l10n_fr_certification/__manifest__.py b/addons/l10n_fr_certification/__manifest__.py
deleted file mode 100644
index 7e2ea327d54..00000000000
--- a/addons/l10n_fr_certification/__manifest__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-{
- 'name': 'France - Accounting - Certified CGI 286 I-3 bis',
- 'version': '1.0',
- 'category': 'Localization',
- 'description': """
-""",
- 'depends': ['l10n_fr'],
- 'installable': True,
- 'auto_install': False,
- 'application': True,
- 'data': [
- 'views/no_cancel.xml',
- 'data/account_move.xml',
- ],
- 'post_init_hook': '_setup_inalterability',
-}
diff --git a/addons/l10n_fr_certification/data/account_move.xml b/addons/l10n_fr_certification/data/account_move.xml
deleted file mode 100644
index 95adc8d8c2e..00000000000
--- a/addons/l10n_fr_certification/data/account_move.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- Intégrité des données comptables
-
- ir.actions.server
-
- action = env['account.move']._check_hash_integrity(env.user.company_id.id)
-
-
-
-
-
-
diff --git a/addons/l10n_fr_certification/models/__init__.py b/addons/l10n_fr_certification/models/__init__.py
deleted file mode 100644
index 8472b2f8bfb..00000000000
--- a/addons/l10n_fr_certification/models/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import res_company
-from . import account
diff --git a/addons/l10n_fr_certification/models/account.py b/addons/l10n_fr_certification/models/account.py
deleted file mode 100644
index bef0880d7ca..00000000000
--- a/addons/l10n_fr_certification/models/account.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# -*- coding: utf-8 -*-
-from hashlib import sha256
-from json import dumps
-
-from openerp import models, api, fields
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-
-ERR_MSG = _("According to the french law, you cannot modify a %s in order for its posted data to be updated or deleted. Field: %s")
-
-#forbidden fields
-MOVE_FIELDS = ['date', 'journal_id', 'company_id']
-LINE_FIELDS = ['debit', 'credit', 'account_id', 'move_id', 'partner_id']
-
-
-class AccountMove(models.Model):
- _inherit = "account.move"
-
- l10n_fr_secure_sequence_number = fields.Integer(readonly=True, copy=False)
- l10n_fr_hash = fields.Char(readonly=True, copy=False)
- l10n_fr_string_to_hash = fields.Char(compute='_compute_string_to_hash', readonly=True, store=False)
-
- def _get_new_hash(self, secure_seq_number):
- """ Returns the hash to write on journal entries when they get posted"""
- self.ensure_one()
- #get the only one exact previous move in the securisation sequence
- prev_move = self.search([('state', '=', 'posted'),
- ('company_id', '=', self.company_id.id),
- ('l10n_fr_secure_sequence_number', '=', int(secure_seq_number) - 1)])
- if prev_move and len(prev_move) != 1:
- raise UserError(
- _('Error occured when computing the hash. Impossible to get the unique previous posted move'))
-
- #build and return the hash
- return self._compute_hash(prev_move.l10n_fr_hash if prev_move else '')
-
- def _compute_hash(self, previous_hash):
- """ Computes the hash of the browse_record given as self, based on the hash
- of the previous record in the company's securisation sequence given as parameter"""
- self.ensure_one()
- hash_string = sha256(previous_hash + self.l10n_fr_string_to_hash)
- return hash_string.hexdigest()
-
- def _compute_string_to_hash(self):
- def _getattrstring(obj, field_str):
- field_value = obj[field_str]
- if obj._fields[field_str].type == 'many2one':
- field_value = field_value.id
- return str(field_value)
-
- for move in self:
- values = {}
- for field in MOVE_FIELDS:
- values[field] = _getattrstring(move, field)
-
- for line in move.line_ids:
- for field in LINE_FIELDS:
- values[field] = _getattrstring(line, field)
- #make the json serialization canonical
- # (https://tools.ietf.org/html/draft-staykov-hu-json-canonical-form-00)
- move.l10n_fr_string_to_hash = dumps(values, sort_keys=True, encoding="utf-8",
- ensure_ascii=True, indent=None,
- separators=(',',':'))
-
- @api.multi
- def write(self, vals):
- has_been_posted = False
- for move in self:
- if move.company_id.country_id.code == 'FR':
- # write the hash and the secure_sequence_number when posting an account.move
- if vals.get('state') == 'posted':
- has_been_posted = True
-
- # restrict the operation in case we are trying to write a forbidden field
- if (move.state == "posted" and set(vals).intersection(MOVE_FIELDS)):
- raise UserError(ERR_MSG % (self._name, ', '.join(MOVE_FIELDS)))
- # restrict the operation in case we are trying to overwrite existing hash
- if (move.l10n_fr_hash and 'l10n_fr_hash' in vals) or (move.l10n_fr_secure_sequence_number and 'l10n_fr_secure_sequence_number' in vals):
- raise UserError(_('You cannot overwrite the values ensuring the inalterability of the accounting.'))
- res = super(AccountMove, self).write(vals)
- # write the hash and the secure_sequence_number when posting an account.move
- if has_been_posted:
- for move in self.filtered(lambda m: m.company_id.country_id.code == 'FR' and
- not (m.l10n_fr_secure_sequence_number or m.l10n_fr_hash)):
- new_number = move.company_id.l10n_fr_secure_sequence_id.next_by_id()
- vals_hashing = {'l10n_fr_secure_sequence_number': new_number,
- 'l10n_fr_hash': move._get_new_hash(new_number)}
- res |= super(AccountMove, move).write(vals_hashing)
- return res
-
- def button_cancel(self):
- #by-pass the normal behavior/message that tells people can cancel a posted journal entry
- #if the journal allows it.
- if self.company_id.country_id.code == 'FR':
- raise UserError(_('You cannot modify a posted entry of a journal.'))
- super(AccountMove, self).button_cancel()
-
- @api.model
- def _check_hash_integrity(self, company_id):
- """Checks that all posted moves have still the same data as when they were posted
- and raises an error with the result.
- """
- moves = self.search([('state', '=', 'posted'),
- ('company_id', '=', company_id),
- ('l10n_fr_secure_sequence_number', '!=', 0)],
- order="l10n_fr_secure_sequence_number ASC")
-
- if not moves:
- raise UserError(_('Warning: impossible to find any move with a hash.'))
- previous_hash = ''
- start_move_info = []
- for move in moves:
- if move.l10n_fr_hash != move._compute_hash(previous_hash=previous_hash):
- raise UserError(_('Corrupted Data on move %s.') % move.id)
- if not previous_hash:
- #save the date and sequence number of the first move hashed
- start_move_info = [move.date, move.l10n_fr_secure_sequence_number]
- previous_hash = move.l10n_fr_hash
- end_move_info = [move.date, move.l10n_fr_secure_sequence_number]
- raise UserError(_('''Successfully checked the integrity of account moves.
-
- The account moves are guaranteed to be in their original and inalterable state
- - since: %s (Sequence Number: %s)
- - to: %s (Sequence Number: %s)'''
- ) % (start_move_info[0], start_move_info[1], end_move_info[0], end_move_info[1]))
-
-
-class AccountMoveLine(models.Model):
- _inherit = "account.move.line"
-
- @api.multi
- def write(self, vals):
- # restrict the operation in case we are trying to write a forbidden field
- if set(vals).intersection(LINE_FIELDS):
- if any(l.company_id.country_id.code == 'FR' and l.move_id.state == 'posted' for l in self):
- raise UserError(ERR_MSG % (self._name, ', '.join(LINE_FIELDS)))
- return super(AccountMoveLine, self).write(vals)
-
-
-class AccountJournal(models.Model):
- _inherit = "account.journal"
-
- @api.multi
- def write(self, vals):
- # restrict the operation in case we are trying to write a forbidden field
- if self.company_id.country_id.code == 'FR' and vals.get('update_posted'):
- raise UserError(ERR_MSG % (self._name, 'update_posted'))
- return super(AccountJournal, self).write(vals)
-
- @api.model
- def create(self, vals):
- # restrict the operation in case we are trying to set a forbidden field
- if self.company_id.country_id.code == 'FR' and vals.get('update_posted'):
- raise UserError(ERR_MSG % (self._name, 'update_posted'))
- return super(AccountJournal, self).create(vals)
diff --git a/addons/l10n_fr_certification/models/res_company.py b/addons/l10n_fr_certification/models/res_company.py
deleted file mode 100644
index 66a2a633164..00000000000
--- a/addons/l10n_fr_certification/models/res_company.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp import fields, models, api
-
-class ResCompany(models.Model):
- _inherit = 'res.company'
-
- l10n_fr_secure_sequence_id = fields.Many2one('ir.sequence', 'Sequence to use to ensure the securisation of data', readonly=True)
-
- @api.model
- def create(self, vals):
- company = super(ResCompany, self).create(vals)
- #when creating a new french company, create the securisation sequence as well
- if company.country_id == self.env.ref('base.fr'):
- company._create_secure_sequence()
- return company
-
- @api.multi
- def write(self, vals):
- res = super(ResCompany, self).write(vals)
- #if country changed to fr, create the securisation sequence
- if vals.get('country_id') and vals.get('country_id') == self.env.ref('base.fr').id:
- self.filtered(lambda c: not c.l10n_fr_secure_sequence_id)._create_secure_sequence()
- return res
-
- def _create_secure_sequence(self):
- """This function creates a no_gap sequence on each companies in self that will ensure
- a unique number is given to all posted account.move in such a way that we can always
- find the previous move of a journal entry.
- """
- for company in self:
- vals = {
- 'name': 'French Securisation of account_move_line - ' + company.name,
- 'code': 'FRSECUR',
- 'implementation': 'no_gap',
- 'prefix': '',
- 'suffix': '',
- 'padding': 0,
- 'company_id': company.id}
- seq = self.env['ir.sequence'].create(vals)
- company.write({'l10n_fr_secure_sequence_id': seq.id})
diff --git a/addons/l10n_fr_certification/views/no_cancel.xml b/addons/l10n_fr_certification/views/no_cancel.xml
deleted file mode 100644
index 43bc66d6ee3..00000000000
--- a/addons/l10n_fr_certification/views/no_cancel.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- bank.statement.draft.form.inherit
- account.bank.statement
-
-
-
- 1
-
-
-
-
-
- invoice.form.cancel.inherit
- account.invoice
-
-
-
- 1
-
-
-
-
-
- account.move.form.nocancel.inherit
- account.move
-
-
-
- 1
-
-
-
-
-
-