diff options
| author | Pierre Masereel <pim@odoo.com> | 2019-09-25 12:54:54 +0000 |
|---|---|---|
| committer | Pierre Masereel <pim@odoo.com> | 2019-10-01 10:02:50 +0000 |
| commit | 82e06a9f5086e5417c5371d592c99153db2dcac4 (patch) | |
| tree | 2bf0812a2e92b3ed77372f62371dad1d4f79ca96 /addons | |
| parent | 78994b55b6241ea95feb45b4b2826de5aabbb48c (diff) | |
[FIX] l10n_fr_pos_cert: inalterability check report
We've added a repport that guarantee that the blockchain of hash has not
been altered. It recompute the hash and check that they are the same as
the one stored.
TASK-ID: 47990
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/l10n_fr_pos_cert/__init__.py | 1 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/models/res_company.py | 54 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/report/__init__.py | 4 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/report/pos_hash_integrity.py | 22 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/report/pos_hash_integrity.xml | 95 | ||||
| -rw-r--r-- | addons/l10n_fr_pos_cert/views/pos_inalterability_menuitem.xml | 25 |
7 files changed, 198 insertions, 4 deletions
diff --git a/addons/l10n_fr_pos_cert/__init__.py b/addons/l10n_fr_pos_cert/__init__.py index 4059582d30c..c4104881ea0 100644 --- a/addons/l10n_fr_pos_cert/__init__.py +++ b/addons/l10n_fr_pos_cert/__init__.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import report from odoo import api, SUPERUSER_ID diff --git a/addons/l10n_fr_pos_cert/__manifest__.py b/addons/l10n_fr_pos_cert/__manifest__.py index e750607a30f..9e9dce4d0cf 100644 --- a/addons/l10n_fr_pos_cert/__manifest__.py +++ b/addons/l10n_fr_pos_cert/__manifest__.py @@ -30,6 +30,7 @@ The module adds following features: 'views/account_views.xml', 'views/l10n_fr_pos_cert_templates.xml', 'views/pos_views.xml', + 'report/pos_hash_integrity.xml', ], 'qweb': ['static/src/xml/pos.xml'], 'post_init_hook': '_setup_inalterability', diff --git a/addons/l10n_fr_pos_cert/models/res_company.py b/addons/l10n_fr_pos_cert/models/res_company.py index 74dae578af3..5cacda61c1d 100644 --- a/addons/l10n_fr_pos_cert/models/res_company.py +++ b/addons/l10n_fr_pos_cert/models/res_company.py @@ -3,7 +3,8 @@ from odoo import models, api, fields, _ from odoo.exceptions import UserError from datetime import datetime -from odoo.fields import Datetime +from odoo.fields import Datetime, Date +from odoo.tools.misc import format_date import pytz @@ -42,3 +43,54 @@ class ResCompany(models.Model): sequence_fields = ['l10n_fr_pos_cert_sequence_id'] company._create_secure_sequence(sequence_fields) return res + + def _action_check_pos_hash_integrity(self): + return self.env.ref('l10n_fr_pos_cert.action_report_pos_hash_integrity').report_action(self.id) + + def _check_pos_hash_integrity(self): + """Checks that all posted or invoiced pos orders have still the same data as when they were posted + and raises an error with the result. + """ + def build_order_info(order): + entry_reference = _('(Receipt ref.: %s)') + order_reference_string = order.pos_reference and entry_reference % order.pos_reference or '' + return [ctx_tz(order, 'date_order'), order.l10n_fr_hash, order.name, order_reference_string, ctx_tz(order, 'write_date')] + + hash_verified = True + msg_alert = '' + report_dict = {} + if self._is_accounting_unalterable(): + orders = self.env['pos.order'].search([('state', 'in', ['paid', 'done', 'invoiced']), ('company_id', '=', self.id), + ('l10n_fr_secure_sequence_number', '!=', 0)], order="l10n_fr_secure_sequence_number ASC") + + if not orders: + msg_alert = (_('There isn\'t any order flagged for data inalterability yet for the company %s. This mechanism only runs for point of sale orders generated after the installation of the module France - Certification CGI 286 I-3 bis. - POS') % self.env.company.name) + hash_verified = False + + previous_hash = u'' + start_order_info = [] + for order in orders: + if order.l10n_fr_hash != order._compute_hash(previous_hash=previous_hash): + msg_alert = (_('Corrupted data on point of sale order with id %s.') % order.id) + hash_verified = False + break + previous_hash = order.l10n_fr_hash + + if hash_verified: + orders_sorted_date = orders.sorted(lambda o: o.date_order) + start_order_info = build_order_info(orders_sorted_date[0]) + end_order_info = build_order_info(orders_sorted_date[-1]) + + report_dict.update({ + 'first_order_name': start_order_info[2], + 'first_order_hash': start_order_info[1], + 'first_order_date': start_order_info[0], + 'last_order_name': end_order_info[2], + 'last_order_hash': end_order_info[1], + 'last_order_date': end_order_info[0], + }) + return { + 'result': hash_verified and report_dict or 'None', + 'msg_alert': msg_alert or 'None', + 'printing_date': format_date(self.env, Date.to_string( Date.today())), + } diff --git a/addons/l10n_fr_pos_cert/report/__init__.py b/addons/l10n_fr_pos_cert/report/__init__.py new file mode 100644 index 00000000000..af5f8a80860 --- /dev/null +++ b/addons/l10n_fr_pos_cert/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import pos_hash_integrity diff --git a/addons/l10n_fr_pos_cert/report/pos_hash_integrity.py b/addons/l10n_fr_pos_cert/report/pos_hash_integrity.py new file mode 100644 index 00000000000..d7c36038022 --- /dev/null +++ b/addons/l10n_fr_pos_cert/report/pos_hash_integrity.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class ReportPosHashIntegrity(models.AbstractModel): + _name = 'report.l10n_fr_pos_cert.report_pos_hash_integrity' + _description = 'Get french pos hash integrity result as PDF.' + + @api.model + def _get_report_values(self, docids, data=None): + if data: + data.update(self.env.company._check_pos_hash_integrity()) + else: + data = self.env.company._check_hash_pos_integrity() + return { + 'doc_ids' : docids, + 'doc_model' : self.env['res.company'], + 'data' : data, + 'docs' : self.env['res.company'].browse(self.env.company.id), + } diff --git a/addons/l10n_fr_pos_cert/report/pos_hash_integrity.xml b/addons/l10n_fr_pos_cert/report/pos_hash_integrity.xml new file mode 100644 index 00000000000..a64b3d9a0a8 --- /dev/null +++ b/addons/l10n_fr_pos_cert/report/pos_hash_integrity.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <data> + <template id="report_pos_hash_integrity"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="company"> + <t t-call="web.external_layout"> + <div class="page"> + <div class="row" id="hash_header"> + <div class="col-12"> + <br/> + <h2>Résultat du test d'intégrité - <span t-esc="data['printing_date']"/></h2> + <br/> + </div> + </div> + <div class="row"> + <div class="col-12" id="hash_config_review"> + <h6> + Selon l’article 286 du code général des impôts français, toute livraison de bien ou prestation + de services ne donnant pas lieu à facturation et étant enregistrée au moyen d’un logiciel ou + d’un système de caisse doit satisfaire à des conditions d’inaltérabilité et de sécurisation des + données en vue d’un contrôle de l’administration fiscale. + <br/> + <br/> + Ces conditions sont respectées via une fonction de hachage des ventes du Point de Vente. + <br/> + <br/> + </h6> + </div> + </div> + <t t-if="data['result'] != 'None'"> + <div class="row"> + <div class="col-12" id="hash_data_consistency"> + <br/> + <h3>Contrôle des données du point de vente</h3> + <br/> + <t t-if="data['result'] != 'None' and data['msg_alert'] == 'None'"> + <h5> + Toutes les ventes effectuées via le Point de Vente + sont bien dans la chaîne de hachage. + </h5> + <br/> + </t> + </div> + </div> + <div class="row"> + <div class="col-12" id="hash_data_consistency_table"> + <table class="table table-bordered" style="table-layout: fixed"> + <thead style="display: table-row-group"> + <tr> + <th class="text-center" style="width: 25%" scope="col">First Hash</th> + <th class="text-center" style="width: 25%" scope="col">First Entry</th> + <th class="text-center" style="width: 25%" scope="col">Last Hash</th> + <th class="text-center" style="width: 25%" scope="col">Last Entry</th> + </tr> + </thead> + <tbody> + <t t-if="data['result'] != 'None'"> + <t t-if="data['result']['first_order_hash'] != 'None'"> + <tr> + <td><span t-esc="data['result']['first_order_hash']"/></td> + <td> + <span t-esc="data['result']['first_order_name']"/> <br/> + <span t-esc="data['result']['first_order_date']"/> + </td> + <td><span t-esc="data['result']['last_order_hash']"/></td> + <td> + <span t-esc="data['result']['last_order_name']"/> <br/> + <span t-esc="data['result']['last_order_date']"/> + </td> + </tr> + </t> + </t> + </tbody> + </table> + </div> + </div> + <div class="row" id="hash_last_div"> + <div class="col-12" id="hash_chain_compliant"> + <br/> + <h6> + La chaîne de hachage est conforme: il n’est pas possible d’altérer les données + sans casser la chaîne de hachage pour les pièces ultérieures. + </h6> + <br/> + </div> + </div> + </t> + </div> + </t> + </t> + </t> + </template> + </data> +</odoo> diff --git a/addons/l10n_fr_pos_cert/views/pos_inalterability_menuitem.xml b/addons/l10n_fr_pos_cert/views/pos_inalterability_menuitem.xml index 4f05afcb99f..efaab7ef24f 100644 --- a/addons/l10n_fr_pos_cert/views/pos_inalterability_menuitem.xml +++ b/addons/l10n_fr_pos_cert/views/pos_inalterability_menuitem.xml @@ -1,4 +1,23 @@ <odoo> - <menuitem id="pos_fr_statements_menu" name="French Statements" parent="point_of_sale.menu_point_rep" sequence="9" /> - <menuitem action="l10n_fr.action_list_view_account_sale_closing" id="menu_account_closing" parent="pos_fr_statements_menu" sequence="90"/> -</odoo>
\ No newline at end of file + <report + id="action_report_pos_hash_integrity" + model="res.company" + string="Hash integrity result PDF" + report_type="qweb-pdf" + name="l10n_fr_pos_cert.report_pos_hash_integrity" + file="l10n_fr_pos_cert.report_pos_hash_integrity" + menu="False" + /> + <record model="ir.actions.server" id="action_check_pos_hash_integrity"> + <field name="name">POS Inalterability Check</field> + <field name="model_id" ref="account.model_res_company"/> + <field name="type">ir.actions.server</field> + <field name="state">code</field> + <field name="code"> + action = env.company._action_check_pos_hash_integrity() + </field> + </record> + <menuitem id="pos_fr_statements_menu" name="French Statements" parent="point_of_sale.menu_point_rep" sequence="9" /> + <menuitem action="l10n_fr.action_list_view_account_sale_closing" id="menu_account_closing" parent="pos_fr_statements_menu" sequence="80"/> + <menuitem action="l10n_fr_pos_cert.action_check_pos_hash_integrity" id="menu_check_move_integrity_reporting" parent="pos_fr_statements_menu" sequence="90"/> +</odoo> |
