mirror of
https://github.com/bringout/oca-ocb-l10n_europe.git
synced 2026-04-26 19:42:06 +02:00
19.0 vanilla
This commit is contained in:
parent
ff721d030e
commit
7721452493
1826 changed files with 124775 additions and 274114 deletions
|
|
@ -25,36 +25,15 @@ pip install odoo-bringout-oca-ocb-l10n_fr_pos_cert
|
|||
|
||||
## Dependencies
|
||||
|
||||
This addon depends on:
|
||||
- l10n_fr
|
||||
- l10n_fr_account
|
||||
- point_of_sale
|
||||
|
||||
## Manifest Information
|
||||
|
||||
- **Name**: France - VAT Anti-Fraud Certification for Point of Sale (CGI 286 I-3 bis)
|
||||
- **Version**: 1.1
|
||||
- **Category**: Accounting/Localizations/Point of Sale
|
||||
- **License**: LGPL-3
|
||||
- **Installable**: True
|
||||
|
||||
## Source
|
||||
|
||||
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_fr_pos_cert`.
|
||||
- Repository: https://github.com/OCA/OCB
|
||||
- Branch: 19.0
|
||||
- Path: addons/l10n_fr_pos_cert
|
||||
|
||||
## License
|
||||
|
||||
This package maintains the original LGPL-3 license from the upstream Odoo project.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Overview: doc/OVERVIEW.md
|
||||
- Architecture: doc/ARCHITECTURE.md
|
||||
- Models: doc/MODELS.md
|
||||
- Controllers: doc/CONTROLLERS.md
|
||||
- Wizards: doc/WIZARDS.md
|
||||
- Install: doc/INSTALL.md
|
||||
- Usage: doc/USAGE.md
|
||||
- Configuration: doc/CONFIGURATION.md
|
||||
- Dependencies: doc/DEPENDENCIES.md
|
||||
- Troubleshooting: doc/TROUBLESHOOTING.md
|
||||
- FAQ: doc/FAQ.md
|
||||
This package preserves the original LGPL-3 license.
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@
|
|||
|
||||
from . import models
|
||||
from . import report
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
|
||||
def _setup_inalterability(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
def _setup_inalterability(env):
|
||||
# enable ping for this module
|
||||
env['publisher_warranty.contract'].update_notification(cron_mode=True)
|
||||
|
||||
fr_companies = env['res.company'].search([('partner_id.country_id.code', 'in', env['res.company']._get_unalterable_country())])
|
||||
fr_companies = env['res.company'].search([('partner_id.country_id.code', 'in', env['res.company']._get_france_country_codes())])
|
||||
if fr_companies:
|
||||
fr_companies._create_secure_sequence(['l10n_fr_pos_cert_sequence_id'])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
{
|
||||
'name': 'France - VAT Anti-Fraud Certification for Point of Sale (CGI 286 I-3 bis)',
|
||||
'icon': '/l10n_fr/static/description/icon.png',
|
||||
'version': '1.1',
|
||||
'category': 'Accounting/Localizations/Point of Sale',
|
||||
'description': """
|
||||
|
|
@ -22,7 +21,7 @@ The module adds following features:
|
|||
|
||||
Access to download the mandatory Certificate of Conformity delivered by Odoo SA (only for Odoo Enterprise users)
|
||||
""",
|
||||
'depends': ['l10n_fr', 'point_of_sale'],
|
||||
'depends': ['l10n_fr_account', 'point_of_sale'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'data': [
|
||||
|
|
@ -37,11 +36,16 @@ The module adds following features:
|
|||
],
|
||||
'post_init_hook': '_setup_inalterability',
|
||||
'assets': {
|
||||
'point_of_sale.assets': [
|
||||
'l10n_fr_pos_cert/static/src/js/**/*',
|
||||
'l10n_fr_pos_cert/static/src/css/pos.css',
|
||||
'l10n_fr_pos_cert/static/src/xml/**/*',
|
||||
'web.assets_unit_tests': [
|
||||
'l10n_fr_pos_cert/static/tests/unit/**/*',
|
||||
],
|
||||
'point_of_sale._assets_pos': [
|
||||
'l10n_fr_pos_cert/static/src/**/*',
|
||||
],
|
||||
'web.assets_tests': [
|
||||
'l10n_fr_pos_cert/static/tests/tours/**/*',
|
||||
],
|
||||
},
|
||||
'author': 'Odoo S.A.',
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
<field name="name">Generate Daily Sales Closing</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model_id" ref="model_account_sale_closing"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._automated_closing('daily')</field>
|
||||
|
|
@ -14,8 +12,6 @@
|
|||
<field name="name">Generate Monthly Sales Closing</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">months</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model_id" ref="model_account_sale_closing"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._automated_closing('monthly')</field>
|
||||
|
|
@ -25,8 +21,6 @@
|
|||
<field name="name">Generate Annual Sales Closing</field>
|
||||
<field name="interval_number">12</field>
|
||||
<field name="interval_type">months</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model_id" ref="model_account_sale_closing"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._automated_closing('annually')</field>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-10-30 09:34+0000\n"
|
||||
"PO-Revision-Date: 2024-10-30 09:34+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 19:06+0000\n"
|
||||
"PO-Revision-Date: 2024-11-04 08:46+0000\n"
|
||||
"Last-Translator: Manon Rondou <ronm@odoo.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
|
|
@ -19,22 +19,19 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "(Receipt ref.: %s)"
|
||||
msgstr "(Réf. reçu : %s)"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml:0
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/Orderline.xml:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderSummary.xml:0
|
||||
msgid "/ Units"
|
||||
msgstr "/ Unité"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid "According to French law, you cannot delete a point of sale order."
|
||||
msgstr ""
|
||||
"Selon la loi française, vous ne pouvez pas supprimer une commande de caisse."
|
||||
|
|
@ -42,17 +39,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a %s. Forbidden fields: %s."
|
||||
msgstr ""
|
||||
"Selon la loi française, vous ne pouvez pas modifier un %s. Champs "
|
||||
"interdits : %s."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a point of sale order line. "
|
||||
"Forbidden fields: %s."
|
||||
|
|
@ -63,7 +49,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a point of sale order. "
|
||||
"Forbidden fields: %s."
|
||||
|
|
@ -74,12 +59,11 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.form_view_account_sale_closing
|
||||
msgid "Account Closing"
|
||||
msgstr "Clotûre de Compte"
|
||||
msgstr "Clotûre de compte"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Accounting is not unalterable for the company %s. This mechanism is designed "
|
||||
"for companies where accounting is unalterable."
|
||||
|
|
@ -90,7 +74,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An error occurred when computing the inalterability. Impossible to get the "
|
||||
"unique previous posted point of sale order."
|
||||
|
|
@ -99,13 +82,6 @@ msgstr ""
|
|||
"Impossible de récupérer la dernière commande de caisse unique et "
|
||||
"comptabilisée."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "An unknown error prevents us from getting closing information."
|
||||
msgstr "Une erreur inconnue empêche d'obtenir les informations de fermeture."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__annually
|
||||
msgid "Annual"
|
||||
|
|
@ -114,10 +90,14 @@ msgstr "Annuelle"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Annual Closing"
|
||||
msgstr "Clôture annuelle"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Aucune donnée corrompue n'a été détectée."
|
||||
msgstr "Aucune donnée corrompue n'a été détectée."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Closing Date"
|
||||
|
|
@ -138,6 +118,11 @@ msgstr "Sociétés"
|
|||
msgid "Company"
|
||||
msgstr "Société"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Contexte"
|
||||
msgstr "Contexte"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Contrôle des données du point de vente"
|
||||
|
|
@ -146,7 +131,6 @@ msgstr "Contrôle des données du point de vente"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Corrupted data on point of sale order with id %s."
|
||||
msgstr "Données corrompues sur la commande de caisse avec l'id %s."
|
||||
|
||||
|
|
@ -163,7 +147,7 @@ msgstr "Créé le"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__cumulative_total
|
||||
msgid "Cumulative Grand Total"
|
||||
msgstr "Grant Total Cumulé"
|
||||
msgstr "Grand total cumulé"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__currency_id
|
||||
|
|
@ -178,9 +162,13 @@ msgstr "Journalière"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Daily Closing"
|
||||
msgstr "Clôture Journalière"
|
||||
msgstr "Clôture journalière"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__date_closing_start
|
||||
|
|
@ -193,7 +181,19 @@ msgid "Date to which the values are computed"
|
|||
msgstr "Date jusqu'à laquelle les valeurs sont calculées"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Dernière transaction"
|
||||
msgstr "Dernière transaction"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_fiscal_position__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_config__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order_line__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_session__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_report_l10n_fr_pos_cert_report_pos_hash_integrity__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nom affiché"
|
||||
|
||||
|
|
@ -202,16 +202,6 @@ msgstr "Nom affiché"
|
|||
msgid "Données corrompues sur la commande du point de vente:"
|
||||
msgstr "Données corrompues sur la commande du point de vente:"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "First Entry"
|
||||
msgstr "Première entrée"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "First Hash"
|
||||
msgstr "Premier hachage"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_account_fiscal_position
|
||||
msgid "Fiscal Position"
|
||||
|
|
@ -229,19 +219,16 @@ msgstr "Fréquence et numéro de séquence unique"
|
|||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_annually_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_annually
|
||||
msgid "Generate Annual Sales Closing"
|
||||
msgstr "Générer la clôture annuelle des ventes"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_daily_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_daily
|
||||
msgid "Generate Daily Sales Closing"
|
||||
msgstr "Générer la clôture journalière des ventes"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_monthly_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_monthly
|
||||
msgid "Generate Monthly Sales Closing"
|
||||
msgstr "Générer la clôture mensuelle des ventes"
|
||||
|
||||
|
|
@ -252,6 +239,11 @@ msgstr ""
|
|||
"Obtenir le résultat de la vérication d'intégrité de la caisse française en "
|
||||
"PDF."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Hachage"
|
||||
msgstr "Hachage"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.pos_order_form_inherit
|
||||
msgid "Hash"
|
||||
|
|
@ -263,7 +255,14 @@ msgid "Hash integrity result PDF"
|
|||
msgstr "Résultat d'intégrité en PDF"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_fiscal_position__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_config__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order_line__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_session__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_report_l10n_fr_pos_cert_report_pos_hash_integrity__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
|
|
@ -290,30 +289,25 @@ msgstr "Texte à hacher"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"La chaîne de hachage est conforme: il n’est pas possible d’altérer les "
|
||||
"données\n"
|
||||
" sans casser la chaîne de hachage "
|
||||
"pour les pièces ultérieures."
|
||||
"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érieurs"
|
||||
msgstr ""
|
||||
"La chaîne de hachage est conforme: il n’est pas possible d’altérer les "
|
||||
"données\n"
|
||||
" sans casser la chaîne de hachage "
|
||||
"pour les pièces ultérieures."
|
||||
"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érieurs"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Last Entry"
|
||||
msgstr "Dernière entrée"
|
||||
msgid "La date (année-mois-jour-heure-minute)"
|
||||
msgstr "La date (année-mois-jour-heure-minute)"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Last Hash"
|
||||
msgstr "Dernier hachage"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Dernière modification le"
|
||||
msgid ""
|
||||
"La fonction de hachage garantit que les données suivates des transactions "
|
||||
"sont inaltérables:"
|
||||
msgstr ""
|
||||
"La fonction de hachage garantit que les données suivates des transactions "
|
||||
"sont inaltérables:"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__last_order_hash
|
||||
|
|
@ -340,10 +334,33 @@ msgstr "Dernière mise à jour par"
|
|||
msgid "Last Updated on"
|
||||
msgstr "Dernière mise à jour le"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Le détail des articles ou prestations (libellé, quantité, prix unitaire, "
|
||||
"total hors taxes de la ligne, taux de TVA associé)"
|
||||
msgstr ""
|
||||
"Le détail des articles ou prestations (libellé, quantité, prix unitaire, "
|
||||
"total hors taxes de la ligne, taux de TVA associé)"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le montant total toutes taxes comprises"
|
||||
msgstr "Le montant total toutes taxes comprises"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le numéro de caisse"
|
||||
msgstr "Le numéro de caisse"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le numéro du justificatif"
|
||||
msgstr "Le numéro du justificatif"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/app/services/pos.js:0
|
||||
msgid "Missing Country"
|
||||
msgstr "Pays manquant"
|
||||
|
||||
|
|
@ -355,7 +372,6 @@ msgstr "Mensuelle"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Monthly Closing"
|
||||
msgstr "Clôture mensuelle"
|
||||
|
||||
|
|
@ -364,20 +380,12 @@ msgstr "Clôture mensuelle"
|
|||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Network Error"
|
||||
msgstr "Erreur réseau"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml:0
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/Orderline.xml:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderSummary.xml:0
|
||||
msgid "Old unit price:"
|
||||
msgstr "Anciennement:"
|
||||
msgstr "Anciennement :"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.action_check_pos_hash_integrity
|
||||
|
|
@ -390,13 +398,6 @@ msgstr "Vérification d'inaltérabilité de la caisse"
|
|||
msgid "Period Total"
|
||||
msgstr "Total sur la période"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Please check your internet connection and try again."
|
||||
msgstr "Veuillez vérifier votre connexion internet et réessayez."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_config
|
||||
msgid "Point of Sale Configuration"
|
||||
|
|
@ -417,6 +418,31 @@ msgstr "Commandes du point de vente"
|
|||
msgid "Point of Sale Session"
|
||||
msgstr "Session du point de vente"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__pos_version
|
||||
msgid "Pos Version"
|
||||
msgstr "Version du PdV"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Première transaction"
|
||||
msgstr "Première transaction"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__previous_order_id
|
||||
msgid "Previous Order"
|
||||
msgstr "Commande précédente"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Réf. Commande"
|
||||
msgstr "Réf. Commande"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Résultat du test"
|
||||
msgstr "Résultat du test"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Résultat du test d'intégrité -"
|
||||
|
|
@ -430,7 +456,6 @@ msgstr "Clôture des ventes"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Sale Closings are not meant to be written or deleted under any circumstances."
|
||||
msgstr "Les clôtures de ventes ne peuvent ni être modifiées ni supprimées."
|
||||
|
|
@ -457,14 +482,14 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Selon l’article 286 du code général des impôts français, toute livraison de "
|
||||
"Selon l'article 286 du code général des impôts français, toute livraison de "
|
||||
"bien ou prestation\n"
|
||||
" de services ne donnant pas lieu à "
|
||||
"facturation et étant enregistrée au moyen d’un logiciel ou\n"
|
||||
" d’un système de caisse doit "
|
||||
"satisfaire à des conditions d’inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d’un contrôle de "
|
||||
"l’administration fiscale.\n"
|
||||
"facturation et étant enregistrée au moyen d'un logiciel ou\n"
|
||||
" d'un système de caisse doit "
|
||||
"satisfaire à des conditions d'inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d'un contrôle de "
|
||||
"l'administration fiscale.\n"
|
||||
" <br/>\n"
|
||||
" <br/>\n"
|
||||
" Ces conditions sont respectées via "
|
||||
|
|
@ -472,14 +497,14 @@ msgid ""
|
|||
" <br/>\n"
|
||||
" <br/>"
|
||||
msgstr ""
|
||||
"Selon l’article 286 du code général des impôts français, toute livraison de "
|
||||
"Selon l'article 286 du code général des impôts français, toute livraison de "
|
||||
"bien ou prestation\n"
|
||||
" de services ne donnant pas lieu à "
|
||||
"facturation et étant enregistrée au moyen d’un logiciel ou\n"
|
||||
" d’un système de caisse doit "
|
||||
"satisfaire à des conditions d’inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d’un contrôle de "
|
||||
"l’administration fiscale.\n"
|
||||
"facturation et étant enregistrée au moyen d'un logiciel ou\n"
|
||||
" d'un système de caisse doit "
|
||||
"satisfaire à des conditions d'inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d'un contrôle de "
|
||||
"l'administration fiscale.\n"
|
||||
" <br/>\n"
|
||||
" <br/>\n"
|
||||
" Ces conditions sont respectées via "
|
||||
|
|
@ -504,8 +529,7 @@ msgstr "Les clôtures sont créées par Odoo"
|
|||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/app/services/pos.js:0
|
||||
msgid "The company %s doesn't have a country set."
|
||||
msgstr "La société %s n'a pas de pays configuré."
|
||||
|
||||
|
|
@ -517,7 +541,6 @@ msgstr "La devis de la société"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"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 "
|
||||
|
|
@ -545,25 +568,27 @@ msgstr "Total sur les comptes débiteurs depuis l'installation de la caisse"
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Toutes les ventes effectuées via le Point de Vente\n"
|
||||
" sont bien dans la chaîne de "
|
||||
"hachage."
|
||||
"Toutes les données liées à la réception du paiement en contrepartie (mode de "
|
||||
"réglement notamment)"
|
||||
msgstr ""
|
||||
"Toutes les ventes effectuées via le Point de Vente\n"
|
||||
" sont bien dans la chaîne de "
|
||||
"hachage."
|
||||
"Toutes les données liées à la réception du paiement en contrepartie (mode de "
|
||||
"réglement notamment)"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Unknown Error"
|
||||
msgstr "Erreur inconnue"
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Toutes les ventes effectuées via le Point de Vente sont bien dans la chaîne "
|
||||
"de hachage."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_pos_order__pos_version
|
||||
msgid "Version of Odoo that created the order"
|
||||
msgstr "Version d'Odoo utilisée pour la création de la commande"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_fiscal_position.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot modify a fiscal position used in a POS order. You should archive "
|
||||
"it and create a new one."
|
||||
|
|
@ -574,7 +599,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot overwrite the values ensuring the inalterability of the point of "
|
||||
"sale."
|
||||
|
|
@ -585,6 +609,5 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid "You have to set a country in your company setting."
|
||||
msgstr "Vous devez définir un pays dans les paramètres de votre entreprise."
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Project-Id-Version: Odoo Server 19.0+e\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-10-30 09:34+0000\n"
|
||||
"PO-Revision-Date: 2024-10-30 09:34+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 19:06+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 19:06+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -18,37 +18,25 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "(Receipt ref.: %s)"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml:0
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/Orderline.xml:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderSummary.xml:0
|
||||
msgid "/ Units"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid "According to French law, you cannot delete a point of sale order."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a %s. Forbidden fields: %s."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a point of sale order line. "
|
||||
"Forbidden fields: %s."
|
||||
|
|
@ -57,7 +45,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"According to the French law, you cannot modify a point of sale order. "
|
||||
"Forbidden fields: %s."
|
||||
|
|
@ -71,7 +58,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Accounting is not unalterable for the company %s. This mechanism is designed"
|
||||
" for companies where accounting is unalterable."
|
||||
|
|
@ -80,19 +66,11 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"An error occurred when computing the inalterability. Impossible to get the "
|
||||
"unique previous posted point of sale order."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "An unknown error prevents us from getting closing information."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__annually
|
||||
msgid "Annual"
|
||||
|
|
@ -101,10 +79,14 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Annual Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Aucune donnée corrompue n'a été détectée."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Closing Date"
|
||||
|
|
@ -125,6 +107,11 @@ msgstr ""
|
|||
msgid "Company"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Contexte"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Contrôle des données du point de vente"
|
||||
|
|
@ -133,7 +120,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Corrupted data on point of sale order with id %s."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -165,10 +151,14 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Daily Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__date_closing_start
|
||||
msgid "Date from which the total interval is computed"
|
||||
|
|
@ -180,7 +170,19 @@ msgid "Date to which the values are computed"
|
|||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Dernière transaction"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_fiscal_position__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_config__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order_line__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_session__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_report_l10n_fr_pos_cert_report_pos_hash_integrity__display_name
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -189,16 +191,6 @@ msgstr ""
|
|||
msgid "Données corrompues sur la commande du point de vente:"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "First Entry"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "First Hash"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_account_fiscal_position
|
||||
msgid "Fiscal Position"
|
||||
|
|
@ -216,19 +208,16 @@ msgstr ""
|
|||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_annually_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_annually
|
||||
msgid "Generate Annual Sales Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_daily_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_daily
|
||||
msgid "Generate Daily Sales Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.account_sale_closing_monthly_ir_actions_server
|
||||
#: model:ir.cron,cron_name:l10n_fr_pos_cert.account_sale_closing_monthly
|
||||
msgid "Generate Monthly Sales Closing"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -237,6 +226,11 @@ msgstr ""
|
|||
msgid "Get french pos hash integrity result as PDF."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Hachage"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.pos_order_form_inherit
|
||||
msgid "Hash"
|
||||
|
|
@ -248,7 +242,14 @@ msgid "Hash integrity result PDF"
|
|||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_fiscal_position__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_config__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order_line__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_session__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_report_l10n_fr_pos_cert_report_pos_hash_integrity__id
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -275,23 +276,20 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"La chaîne de hachage est conforme: il n’est pas possible d’altérer les données\n"
|
||||
" sans casser la chaîne de hachage pour les pièces ultérieures."
|
||||
"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érieurs"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Last Entry"
|
||||
msgid "La date (année-mois-jour-heure-minute)"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Last Hash"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing____last_update
|
||||
msgid "Last Modified on"
|
||||
msgid ""
|
||||
"La fonction de hachage garantit que les données suivates des transactions "
|
||||
"sont inaltérables:"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
|
|
@ -319,10 +317,31 @@ msgstr ""
|
|||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Le détail des articles ou prestations (libellé, quantité, prix unitaire, "
|
||||
"total hors taxes de la ligne, taux de TVA associé)"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le montant total toutes taxes comprises"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le numéro de caisse"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Le numéro du justificatif"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/app/services/pos.js:0
|
||||
msgid "Missing Country"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -334,7 +353,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid "Monthly Closing"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -343,18 +361,10 @@ msgstr ""
|
|||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Network Error"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml:0
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/Orderline.xml:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/xml/OrderSummary.xml:0
|
||||
msgid "Old unit price:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -369,13 +379,6 @@ msgstr ""
|
|||
msgid "Period Total"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Please check your internet connection and try again."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_config
|
||||
msgid "Point of Sale Configuration"
|
||||
|
|
@ -396,6 +399,31 @@ msgstr ""
|
|||
msgid "Point of Sale Session"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__pos_version
|
||||
msgid "Pos Version"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Première transaction"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__previous_order_id
|
||||
msgid "Previous Order"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Réf. Commande"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Résultat du test"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Résultat du test d'intégrité -"
|
||||
|
|
@ -409,8 +437,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_closing.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Sale Closings are not meant to be written or deleted under any "
|
||||
"circumstances."
|
||||
|
|
@ -434,10 +460,10 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Selon l’article 286 du code général des impôts français, toute livraison de bien ou prestation\n"
|
||||
" de services ne donnant pas lieu à facturation et étant enregistrée au moyen d’un logiciel ou\n"
|
||||
" d’un système de caisse doit satisfaire à des conditions d’inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d’un contrôle de l’administration fiscale.\n"
|
||||
"Selon l'article 286 du code général des impôts français, toute livraison de bien ou prestation\n"
|
||||
" de services ne donnant pas lieu à facturation et étant enregistrée au moyen d'un logiciel ou\n"
|
||||
" d'un système de caisse doit satisfaire à des conditions d'inaltérabilité et de sécurisation des\n"
|
||||
" données en vue d'un contrôle de l'administration fiscale.\n"
|
||||
" <br/>\n"
|
||||
" <br/>\n"
|
||||
" Ces conditions sont respectées via une fonction de hachage des ventes du Point de Vente.\n"
|
||||
|
|
@ -462,8 +488,7 @@ msgstr ""
|
|||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/app/services/pos.js:0
|
||||
msgid "The company %s doesn't have a country set."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -475,7 +500,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"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 "
|
||||
|
|
@ -497,21 +521,25 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Toutes les ventes effectuées via le Point de Vente\n"
|
||||
" sont bien dans la chaîne de hachage."
|
||||
"Toutes les données liées à la réception du paiement en contrepartie (mode de"
|
||||
" réglement notamment)"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/Chrome.js:0
|
||||
#, python-format
|
||||
msgid "Unknown Error"
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid ""
|
||||
"Toutes les ventes effectuées via le Point de Vente sont bien dans la chaîne "
|
||||
"de hachage."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_pos_order__pos_version
|
||||
msgid "Version of Odoo that created the order"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/account_fiscal_position.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot modify a fiscal position used in a POS order. You should archive "
|
||||
"it and create a new one."
|
||||
|
|
@ -520,7 +548,6 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot overwrite the values ensuring the inalterability of the point of "
|
||||
"sale."
|
||||
|
|
@ -529,6 +556,5 @@ msgstr ""
|
|||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-python
|
||||
#: code:addons/l10n_fr_pos_cert/models/pos.py:0
|
||||
#, python-format
|
||||
msgid "You have to set a country in your company setting."
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import models, api, fields
|
||||
from odoo.fields import Datetime as FieldDateTime
|
||||
from odoo.fields import Datetime as FieldDateTime, Domain
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv.expression import AND
|
||||
|
||||
|
||||
class AccountClosing(models.Model):
|
||||
class AccountSaleClosing(models.Model):
|
||||
"""
|
||||
This object holds an interval total and a grand total of the accounts of type receivable for a company,
|
||||
as well as the last account_move that has been counted in a previous object
|
||||
|
|
@ -42,8 +40,9 @@ class AccountClosing(models.Model):
|
|||
JOIN account_journal j ON aml.journal_id = j.id
|
||||
JOIN account_account acc ON acc.id = aml.account_id
|
||||
JOIN account_move m ON m.id = aml.move_id
|
||||
JOIN res_company move_company ON move_company.id = m.company_id
|
||||
WHERE j.type = 'sale'
|
||||
AND aml.company_id = %(company_id)s
|
||||
AND SPLIT_PART(move_company.parent_path, '/', 1)::int = %(company_id)s
|
||||
AND m.state = 'posted'
|
||||
AND acc.account_type = 'asset_receivable' '''
|
||||
|
||||
|
|
@ -87,12 +86,12 @@ class AccountClosing(models.Model):
|
|||
date_start = previous_closing.create_date
|
||||
cumulative_total += previous_closing.cumulative_total
|
||||
|
||||
domain = [('company_id', '=', company.id), ('state', 'in', ('paid', 'done', 'invoiced'))]
|
||||
domain = Domain('company_id', '=', company.id) & Domain('state', 'in', ('paid', 'done'))
|
||||
if first_order.l10n_fr_secure_sequence_number is not False and first_order.l10n_fr_secure_sequence_number is not None:
|
||||
domain = AND([domain, [('l10n_fr_secure_sequence_number', '>', first_order.l10n_fr_secure_sequence_number)]])
|
||||
domain &= Domain('l10n_fr_secure_sequence_number', '>', first_order.l10n_fr_secure_sequence_number)
|
||||
elif date_start:
|
||||
#the first time we compute the closing, we consider only from the installation of the module
|
||||
domain = AND([domain, [('date_order', '>=', date_start)]])
|
||||
# the first time we compute the closing, we consider only from the installation of the module
|
||||
domain &= Domain('date_order', '>=', date_start)
|
||||
|
||||
orders = self.env['pos.order'].search(domain, order='date_order desc')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import datetime, timedelta
|
||||
from hashlib import sha256
|
||||
from json import dumps
|
||||
|
||||
from odoo import models, api, fields
|
||||
from odoo.fields import Datetime
|
||||
from odoo.tools.translate import _, _lt
|
||||
from odoo.exceptions import UserError
|
||||
from json import dumps, loads
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import models, api, fields, release, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
class pos_config(models.Model):
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PosConfig(models.Model):
|
||||
_inherit = 'pos.config'
|
||||
|
||||
def open_ui(self):
|
||||
|
|
@ -21,13 +21,10 @@ class pos_config(models.Model):
|
|||
if config.company_id._is_accounting_unalterable():
|
||||
if config.current_session_id:
|
||||
config.current_session_id._check_session_timing()
|
||||
return super(pos_config, self).open_ui()
|
||||
|
||||
def _config_sequence_implementation(self):
|
||||
return 'no_gap' if self.env.company._is_accounting_unalterable() else super()._config_sequence_implementation()
|
||||
return super().open_ui()
|
||||
|
||||
|
||||
class pos_session(models.Model):
|
||||
class PosSession(models.Model):
|
||||
_inherit = 'pos.session'
|
||||
|
||||
def _check_session_timing(self):
|
||||
|
|
@ -39,35 +36,61 @@ class pos_session(models.Model):
|
|||
sessions_to_check.filtered(lambda s: s.state == 'opening_control').start_at = fields.Datetime.now()
|
||||
for session in sessions_to_check:
|
||||
session._check_session_timing()
|
||||
return super(pos_session, self).open_frontend_cb()
|
||||
return super().open_frontend_cb()
|
||||
|
||||
|
||||
ORDER_FIELDS = ['date_order', 'user_id', 'lines', 'payment_ids', 'pricelist_id', 'partner_id', 'session_id', 'pos_reference', 'sale_journal', 'fiscal_position_id']
|
||||
ORDER_FIELDS_BEFORE_17_4 = ['date_order', 'user_id', 'lines', 'payment_ids', 'pricelist_id', 'session_id', 'pos_reference', 'sale_journal', 'fiscal_position_id', 'partner_id']
|
||||
ORDER_FIELDS_FROM_17_4 = ['date_order', 'user_id', 'lines', 'payment_ids', 'pricelist_id', 'session_id', 'pos_reference', 'sale_journal', 'fiscal_position_id', 'pos_version']
|
||||
LINE_FIELDS = ['notice', 'product_id', 'qty', 'price_unit', 'discount', 'tax_ids', 'tax_ids_after_fiscal_position']
|
||||
ERR_MSG = _lt('According to the French law, you cannot modify a %s. Forbidden fields: %s.')
|
||||
|
||||
|
||||
class pos_order(models.Model):
|
||||
class PosOrder(models.Model):
|
||||
_inherit = 'pos.order'
|
||||
|
||||
l10n_fr_hash = fields.Char(string="Inalteralbility Hash", readonly=True, copy=False)
|
||||
l10n_fr_secure_sequence_number = fields.Integer(string="Inalteralbility No Gap Sequence #", readonly=True, copy=False)
|
||||
l10n_fr_string_to_hash = fields.Char(compute='_compute_string_to_hash', readonly=True, store=False)
|
||||
previous_order_id = fields.Many2one('pos.order', string='Previous Order', readonly=True, compute='_compute_previous_order', store=True, copy=False)
|
||||
pos_version = fields.Char(help="Version of Odoo that created the order", readonly=True, copy=False)
|
||||
|
||||
def _get_new_hash(self, secure_seq_number):
|
||||
@api.depends('l10n_fr_secure_sequence_number')
|
||||
def _compute_previous_order(self):
|
||||
orders_by_company = defaultdict(list)
|
||||
for order in self.filtered(lambda o: o.l10n_fr_secure_sequence_number):
|
||||
orders_by_company[order.company_id.id].append(order)
|
||||
|
||||
for company_id, orders in orders_by_company.items():
|
||||
# Since sequence number can't be zero, we don't consider
|
||||
# it as a posible previous sequence number
|
||||
prev_seq = [o.l10n_fr_secure_sequence_number - 1 for o in orders if o.l10n_fr_secure_sequence_number > 1]
|
||||
prev_orders = self.search([
|
||||
('state', 'in', ['paid', 'done']),
|
||||
('company_id', '=', company_id),
|
||||
('l10n_fr_secure_sequence_number', 'in', prev_seq),
|
||||
])
|
||||
prev_map = defaultdict(list)
|
||||
for po in prev_orders:
|
||||
prev_map[po.l10n_fr_secure_sequence_number].append(po)
|
||||
|
||||
for order in orders:
|
||||
match = prev_map.get(order.l10n_fr_secure_sequence_number - 1, [])
|
||||
if len(match) > 1:
|
||||
raise UserError(_('An error occurred when computing the inalterability. Impossible to get the unique previous posted point of sale order.'))
|
||||
order.previous_order_id = match[0] if match else False
|
||||
|
||||
def _get_new_hash(self):
|
||||
""" Returns the hash to write on pos orders when they get posted"""
|
||||
self.ensure_one()
|
||||
#get the only one exact previous order in the securisation sequence
|
||||
prev_order = self.search([('state', 'in', ['paid', 'done', 'invoiced']),
|
||||
('company_id', '=', self.company_id.id),
|
||||
('l10n_fr_secure_sequence_number', '!=', 0),
|
||||
('l10n_fr_secure_sequence_number', '=', int(secure_seq_number) - 1)])
|
||||
if prev_order and len(prev_order) != 1:
|
||||
raise UserError(
|
||||
_('An error occurred when computing the inalterability. Impossible to get the unique previous posted point of sale order.'))
|
||||
|
||||
#build and return the hash
|
||||
return self._compute_hash(prev_order.l10n_fr_hash if prev_order else u'')
|
||||
# build and return the hash
|
||||
computed_hash = self._compute_hash(self.previous_order_id.l10n_fr_hash if self.previous_order_id else '')
|
||||
_logger.info(
|
||||
'Computed hash for order ID %s: %s \n String to hash: %s \n Previous hash: %s',
|
||||
self.id,
|
||||
computed_hash,
|
||||
dumps(loads(self.l10n_fr_string_to_hash), indent=2),
|
||||
self.previous_order_id.l10n_fr_hash
|
||||
)
|
||||
return computed_hash
|
||||
|
||||
def _compute_hash(self, previous_hash):
|
||||
""" Computes the hash of the browse_record given as self, based on the hash
|
||||
|
|
@ -91,7 +114,7 @@ class pos_order(models.Model):
|
|||
relational_ids = defaultdict(set)
|
||||
|
||||
for data_list, field_names, field_defs in (
|
||||
(orders_data, ORDER_FIELDS, order_field_defs),
|
||||
(orders_data, fields_to_fetch, order_field_defs),
|
||||
(lines_data, LINE_FIELDS, line_field_defs),
|
||||
):
|
||||
for record in data_list:
|
||||
|
|
@ -108,8 +131,8 @@ class pos_order(models.Model):
|
|||
sorted_relational_ids[model_name] = self.env[model_name].search([('id', 'in', list(ids))]).ids
|
||||
|
||||
return sorted_relational_ids
|
||||
|
||||
orders_data = self.read(ORDER_FIELDS + ['id'], load='')
|
||||
fields_to_fetch = list(set(ORDER_FIELDS_BEFORE_17_4) | set(ORDER_FIELDS_FROM_17_4))
|
||||
orders_data = self.read(fields_to_fetch + ['id'], load='')
|
||||
lines_data = self.lines.read(LINE_FIELDS + ['id', 'order_id'], load='')
|
||||
|
||||
orders_by_id = {order['id']: order for order in orders_data}
|
||||
|
|
@ -121,7 +144,7 @@ class pos_order(models.Model):
|
|||
'type': self._fields[field].type,
|
||||
'comodel': self._fields[field].comodel_name if hasattr(self._fields[field], 'comodel_name') else None
|
||||
}
|
||||
for field in ORDER_FIELDS
|
||||
for field in fields_to_fetch
|
||||
}
|
||||
line_field_defs = {
|
||||
field: {
|
||||
|
|
@ -135,9 +158,15 @@ class pos_order(models.Model):
|
|||
|
||||
for order in self:
|
||||
values = {}
|
||||
if order.pos_version:
|
||||
order_fields = ORDER_FIELDS_FROM_17_4
|
||||
else:
|
||||
order_fields = ORDER_FIELDS_BEFORE_17_4
|
||||
for field in order_fields:
|
||||
values[field] = _getattrstring(order, field)
|
||||
order_data = orders_by_id[order.id]
|
||||
|
||||
for field in ORDER_FIELDS:
|
||||
for field in order_fields:
|
||||
field_def = order_field_defs[field]
|
||||
values[field] = _getattrstring(order_data.get(field), field_def['type'], field_def['comodel'])
|
||||
|
||||
|
|
@ -153,29 +182,38 @@ class pos_order(models.Model):
|
|||
ensure_ascii=True, indent=None,
|
||||
separators=(',',':'))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
vals['pos_version'] = release.version
|
||||
return super().create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
has_been_posted = False
|
||||
for order in self:
|
||||
if order.company_id._is_accounting_unalterable():
|
||||
# write the hash and the secure_sequence_number when posting or invoicing an pos.order
|
||||
if vals.get('state') in ['paid', 'done', 'invoiced']:
|
||||
if vals.get('state') in ['paid', 'done']:
|
||||
has_been_posted = True
|
||||
|
||||
# restrict the operation in case we are trying to write a forbidden field
|
||||
if (order.state in ['paid', 'done', 'invoiced'] and set(vals).intersection(ORDER_FIELDS)):
|
||||
if order.pos_version:
|
||||
ORDER_FIELDS = ORDER_FIELDS_FROM_17_4
|
||||
else:
|
||||
ORDER_FIELDS = ORDER_FIELDS_BEFORE_17_4
|
||||
if (order.state in ['paid', 'done'] and set(vals).intersection(ORDER_FIELDS)):
|
||||
raise UserError(_('According to the French law, you cannot modify a point of sale order. Forbidden fields: %s.') % ', '.join(ORDER_FIELDS))
|
||||
# restrict the operation in case we are trying to overwrite existing hash
|
||||
if (order.l10n_fr_hash and 'l10n_fr_hash' in vals) or (order.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 point of sale.'))
|
||||
res = super(pos_order, self).write(vals)
|
||||
res = super().write(vals)
|
||||
# write the hash and the secure_sequence_number when posting or invoicing a pos order
|
||||
if has_been_posted:
|
||||
for order in self.filtered(lambda o: o.company_id._is_accounting_unalterable() and
|
||||
not (o.l10n_fr_secure_sequence_number or o.l10n_fr_hash)):
|
||||
new_number = order.company_id.l10n_fr_pos_cert_sequence_id.next_by_id()
|
||||
vals_hashing = {'l10n_fr_secure_sequence_number': new_number,
|
||||
'l10n_fr_hash': order._get_new_hash(new_number)}
|
||||
res |= super(pos_order, order).write(vals_hashing)
|
||||
res |= super(PosOrder, order).write({'l10n_fr_secure_sequence_number': new_number})
|
||||
res |= super(PosOrder, order).write({'l10n_fr_hash': order._get_new_hash()})
|
||||
return res
|
||||
|
||||
@api.ondelete(at_uninstall=True)
|
||||
|
|
@ -184,10 +222,6 @@ class pos_order(models.Model):
|
|||
if order.company_id._is_accounting_unalterable():
|
||||
raise UserError(_("According to French law, you cannot delete a point of sale order."))
|
||||
|
||||
def _export_for_ui(self, order):
|
||||
res = super()._export_for_ui(order)
|
||||
res['l10n_fr_hash'] = order.l10n_fr_hash
|
||||
return res
|
||||
|
||||
class PosOrderLine(models.Model):
|
||||
_inherit = "pos.order.line"
|
||||
|
|
@ -195,6 +229,6 @@ class PosOrderLine(models.Model):
|
|||
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._is_accounting_unalterable() and l.order_id.state in ['done', 'invoiced'] for l in self):
|
||||
if any(l.company_id._is_accounting_unalterable() and (l.order_id.account_move or l.order_id.state == 'done') for l in self):
|
||||
raise UserError(_('According to the French law, you cannot modify a point of sale order line. Forbidden fields: %s.') % ', '.join(LINE_FIELDS))
|
||||
return super(PosOrderLine, self).write(vals)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import pytz
|
|||
|
||||
def ctx_tz(record, field):
|
||||
res_lang = None
|
||||
ctx = record._context
|
||||
tz_name = pytz.timezone(ctx.get('tz') or record.env.user.tz or 'UTC')
|
||||
ctx = record.env.context
|
||||
timestamp = Datetime.from_string(record[field])
|
||||
if ctx.get('lang'):
|
||||
res_lang = record.env['res.lang']._lang_get(ctx['lang'])
|
||||
res_lang = record.env['res.lang']._get_data(code=ctx['lang'])
|
||||
if res_lang:
|
||||
timestamp = pytz.utc.localize(timestamp, is_dst=False)
|
||||
return datetime.strftime(timestamp.astimezone(tz_name), res_lang.date_format + ' ' + res_lang.time_format)
|
||||
tz = record.env.tz
|
||||
return datetime.strftime(timestamp.astimezone(tz), res_lang.date_format + ' ' + res_lang.time_format)
|
||||
return Datetime.context_timestamp(record, timestamp)
|
||||
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class ResCompany(models.Model):
|
|||
msg_alert = ''
|
||||
report_dict = {}
|
||||
if self._is_accounting_unalterable():
|
||||
orders = self.with_context(prefetch_fields=False).env['pos.order'].search([('state', 'in', ['paid', 'done', 'invoiced']), ('company_id', '=', self.id),
|
||||
orders = self.with_context(prefetch_fields=False).env['pos.order'].search([('state', 'in', ['paid', 'done']), ('company_id', '=', self.id),
|
||||
('l10n_fr_secure_sequence_number', '!=', 0)], order="l10n_fr_secure_sequence_number ASC")
|
||||
|
||||
if not orders:
|
||||
|
|
@ -96,4 +96,4 @@ class ResCompany(models.Model):
|
|||
'corrupted_orders': corrupted_orders or 'None'
|
||||
}
|
||||
else:
|
||||
raise UserError(_('Accounting is not unalterable for the company %s. This mechanism is designed for companies where accounting is unalterable.') % self.env.company.name)
|
||||
raise UserError(_('Accounting is not unalterable for the company %s. This mechanism is designed for companies where accounting is unalterable.', self.env.company.name))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ReportPosHashIntegrity(models.AbstractModel):
|
||||
class ReportL10n_Fr_Pos_CertReport_Pos_Hash_Integrity(models.AbstractModel):
|
||||
_name = 'report.l10n_fr_pos_cert.report_pos_hash_integrity'
|
||||
_description = 'Get french pos hash integrity result as PDF.'
|
||||
|
||||
|
|
|
|||
|
|
@ -9,17 +9,20 @@
|
|||
<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>
|
||||
<h2>Résultat du test d'intégrité - <span t-out="data['printing_date']"/></h2>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" id="hash_config_review">
|
||||
<br/>
|
||||
<h3>Contexte</h3>
|
||||
<br/>
|
||||
<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.
|
||||
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.
|
||||
|
|
@ -34,64 +37,70 @@
|
|||
<br/>
|
||||
<h3>Contrôle des données du point de vente</h3>
|
||||
<br/>
|
||||
<t t-if="data['result'] != 'None' and data['corrupted_orders'] == 'None'">
|
||||
<h5>
|
||||
Toutes les ventes effectuées via le Point de Vente
|
||||
sont bien dans la chaîne de hachage.
|
||||
</h5>
|
||||
<br/>
|
||||
</t>
|
||||
<ul>
|
||||
<li t-if="data['result'] != 'None' and data['corrupted_orders'] == 'None'">Toutes les ventes effectuées via le Point de Vente sont bien dans la chaîne de hachage.</li>
|
||||
<li>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érieurs</li>
|
||||
<li>La fonction de hachage garantit que les données suivates des transactions sont inaltérables:
|
||||
<ul>
|
||||
<li>Le numéro du justificatif</li>
|
||||
<li>La date (année-mois-jour-heure-minute)</li>
|
||||
<li>Le numéro de caisse</li>
|
||||
<li>Le montant total toutes taxes comprises</li>
|
||||
<li>Le détail des articles ou prestations (libellé, quantité, prix unitaire, total hors taxes de la ligne, taux de TVA associé)</li>
|
||||
<li>Toutes les données liées à la réception du paiement en contrepartie (mode de réglement notamment)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</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">
|
||||
<table class="table table-borderless" style="table-layout: fixed">
|
||||
<thead>
|
||||
<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>
|
||||
<th class="text-center col-2" scope="col"></th>
|
||||
<th class="text-center col-5" scope="col">Première transaction</th>
|
||||
<th class="text-center col-5" scope="col">Dernière transaction</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 t-if="data['result']['first_order_hash'] != 'None'">
|
||||
<tr>
|
||||
<td>Date</td>
|
||||
<td><span t-out="data['result']['first_order_date']"/></td>
|
||||
<td><span t-out="data['result']['last_order_date']"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Réf. Commande</td>
|
||||
<td><span t-out="data['result']['first_order_name']"/></td>
|
||||
<td><span t-out="data['result']['last_order_name']"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hachage</td>
|
||||
<td><span t-out="data['result']['first_order_hash']"/></td>
|
||||
<td><span t-out="data['result']['last_order_hash']"/></td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-12" id="hash_results">
|
||||
<br/>
|
||||
<h3>Résultat du test</h3>
|
||||
<br/>
|
||||
<t t-if="data['corrupted_orders'] != 'None'">
|
||||
<h5>
|
||||
<h5 class="bg-danger">
|
||||
Données corrompues sur la commande du point de vente:
|
||||
</h5>
|
||||
<span t-esc="data['corrupted_orders']"/> <br/>
|
||||
<span t-out="data['corrupted_orders']"/>
|
||||
<br/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<h5 class="bg-success">
|
||||
Aucune donnée corrompue n'a été détectée.
|
||||
</h5>
|
||||
</t>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { PosStore } from "@point_of_sale/app/services/pos_store";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
|
||||
patch(PosStore.prototype, {
|
||||
is_french_country() {
|
||||
const french_countries = ["FR", "MF", "MQ", "NC", "PF", "RE", "GF", "GP", "TF"];
|
||||
if (!this.company.country_id) {
|
||||
this.dialog.add(AlertDialog, {
|
||||
title: _t("Missing Country"),
|
||||
body: _t("The company %s doesn't have a country set.", this.company.name),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return french_countries.includes(this.company.country_id?.code);
|
||||
},
|
||||
canEditPayment(order) {
|
||||
return this.is_french_country() ? false : super.canEditPayment(order);
|
||||
},
|
||||
});
|
||||
|
|
@ -1,41 +1,23 @@
|
|||
odoo.define('l10n_fr_pos_cert.Chrome', function (require) {
|
||||
'use strict';
|
||||
import { Chrome } from "@point_of_sale/app/pos_app";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { ClosePosPopup } from "@point_of_sale/app/components/popups/closing_popup/closing_popup";
|
||||
import { onMounted } from "@odoo/owl";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
const Chrome = require('point_of_sale.Chrome');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const { isConnectionError } = require('point_of_sale.utils');
|
||||
|
||||
const PosFrCertChrome = (Chrome) =>
|
||||
class extends Chrome {
|
||||
async start() {
|
||||
await super.start();
|
||||
if (this.env.pos.is_french_country() && this.env.pos.pos_session.start_at) {
|
||||
const now = Date.now();
|
||||
let limitDate = new Date(this.env.pos.pos_session.start_at);
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
if (limitDate < now) {
|
||||
try {
|
||||
const info = await this.env.pos.getClosePosInfo();
|
||||
this.showPopup('ClosePosPopup', { info: info });
|
||||
} catch (e) {
|
||||
if (isConnectionError(e)) {
|
||||
this.showPopup('OfflineErrorPopup', {
|
||||
title: this.env._t('Network Error'),
|
||||
body: this.env._t('Please check your internet connection and try again.'),
|
||||
});
|
||||
} else {
|
||||
this.showPopup('ErrorPopup', {
|
||||
title: this.env._t('Unknown Error'),
|
||||
body: this.env._t('An unknown error prevents us from getting closing information.'),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
patch(Chrome.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.dialog = useService("dialog");
|
||||
onMounted(async () => {
|
||||
if (this.pos.is_french_country() && this.pos.session.start_at) {
|
||||
const now = Date.now();
|
||||
const limitDate = new Date(this.pos.session.start_at);
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
if (limitDate.getTime() < now) {
|
||||
const info = await this.pos.getClosePosInfo();
|
||||
this.dialog.add(ClosePosPopup, info);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(Chrome, PosFrCertChrome);
|
||||
|
||||
return Chrome;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
odoo.define('l10n_fr_pos_cert.PaymentScreen', function(require) {
|
||||
|
||||
const PaymentScreen = require('point_of_sale.PaymentScreen');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
const session = require('web.session');
|
||||
|
||||
const PosFrPaymentScreen = PaymentScreen => class extends PaymentScreen {
|
||||
async _postPushOrderResolve(order, order_server_ids) {
|
||||
try {
|
||||
if(this.env.pos.is_french_country()) {
|
||||
let result = await this.rpc({
|
||||
model: 'pos.order',
|
||||
method: 'search_read',
|
||||
domain: [['id', 'in', order_server_ids]],
|
||||
fields: ['l10n_fr_hash'],
|
||||
context: session.user_context,
|
||||
});
|
||||
order.set_l10n_fr_hash(result[0].l10n_fr_hash || false);
|
||||
}
|
||||
} finally {
|
||||
return super._postPushOrderResolve(...arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(PaymentScreen, PosFrPaymentScreen);
|
||||
|
||||
return PaymentScreen;
|
||||
});
|
||||
|
|
@ -1,27 +1,18 @@
|
|||
odoo.define('l10n_fr_pos_cert.ClosePosPopup', function (require) {
|
||||
'use strict';
|
||||
import { ClosePosPopup } from "@point_of_sale/app/components/popups/closing_popup/closing_popup";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
const ClosePosPopup = require('point_of_sale.ClosePosPopup');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
|
||||
const PosFrCertClosePopup = (ClosePosPopup) =>
|
||||
class extends ClosePosPopup {
|
||||
sessionIsOutdated() {
|
||||
let isOutdated = false;
|
||||
if (this.env.pos.is_french_country() && this.env.pos.pos_session.start_at) {
|
||||
const now = Date.now();
|
||||
let limitDate = new Date(this.env.pos.pos_session.start_at);
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
isOutdated = limitDate < now;
|
||||
}
|
||||
return isOutdated;
|
||||
}
|
||||
canCancel() {
|
||||
return super.canCancel() && !this.sessionIsOutdated();
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(ClosePosPopup, PosFrCertClosePopup);
|
||||
|
||||
return ClosePosPopup;
|
||||
patch(ClosePosPopup.prototype, {
|
||||
sessionIsOutdated() {
|
||||
let isOutdated = false;
|
||||
if (this.pos.is_french_country() && this.pos.session.start_at) {
|
||||
const now = Date.now();
|
||||
const limitDate = new Date(this.pos.session.start_at);
|
||||
limitDate.setDate(limitDate.getDate() + 1);
|
||||
isOutdated = limitDate < now;
|
||||
}
|
||||
return isOutdated;
|
||||
},
|
||||
canCancel() {
|
||||
return super.canCancel(...arguments) && !this.sessionIsOutdated();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
odoo.define('l10n_fr_pos_cert.TicketScreen', function(require) {
|
||||
'use strict';
|
||||
|
||||
const TicketScreen = require('point_of_sale.TicketScreen');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
|
||||
const PosFrCertTicketScreen = TicketScreen => class extends TicketScreen {
|
||||
shouldHideDeleteButton(order) {
|
||||
return this.env.pos.is_french_country() && !order.is_empty() || super.shouldHideDeleteButton(order);
|
||||
}
|
||||
};
|
||||
Registries.Component.extend(TicketScreen, PosFrCertTicketScreen);
|
||||
|
||||
return PosFrCertTicketScreen;
|
||||
});
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
odoo.define('l10n_fr_pos_cert.pos', function (require) {
|
||||
"use strict";
|
||||
|
||||
const { Gui } = require('point_of_sale.Gui');
|
||||
var { PosGlobalState, Order, Orderline } = require('point_of_sale.models');
|
||||
var core = require('web.core');
|
||||
const Registries = require('point_of_sale.Registries');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
const L10nFrPosGlobalState = (PosGlobalState) => class L10nFrPosGlobalState extends PosGlobalState {
|
||||
is_french_country(){
|
||||
var french_countries = ['FR', 'MF', 'MQ', 'NC', 'PF', 'RE', 'GF', 'GP', 'TF'];
|
||||
if (!this.company.country) {
|
||||
Gui.showPopup("ErrorPopup", {
|
||||
'title': _t("Missing Country"),
|
||||
'body': _.str.sprintf(_t('The company %s doesn\'t have a country set.'), this.company.name),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return _.contains(french_countries, this.company.country.code);
|
||||
}
|
||||
disallowLineQuantityChange() {
|
||||
let result = super.disallowLineQuantityChange(...arguments);
|
||||
let selectedOrderLine = this.selectedOrder.get_selected_orderline();
|
||||
//Note: is_reward_line is a field in the pos_loyalty module
|
||||
if (selectedOrderLine && selectedOrderLine.is_reward_line) {
|
||||
//Always allow quantity change for reward lines
|
||||
return false || result;
|
||||
}
|
||||
return this.is_french_country() || result;
|
||||
}
|
||||
}
|
||||
Registries.Model.extend(PosGlobalState, L10nFrPosGlobalState);
|
||||
|
||||
|
||||
const L10nFrOrder = (Order) => class L10nFrOrder extends Order {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.l10n_fr_hash = this.l10n_fr_hash || false;
|
||||
this.save_to_db();
|
||||
}
|
||||
export_for_printing() {
|
||||
var result = super.export_for_printing(...arguments);
|
||||
result.l10n_fr_hash = this.get_l10n_fr_hash();
|
||||
return result;
|
||||
}
|
||||
set_l10n_fr_hash (l10n_fr_hash){
|
||||
this.l10n_fr_hash = l10n_fr_hash;
|
||||
}
|
||||
get_l10n_fr_hash() {
|
||||
return this.l10n_fr_hash;
|
||||
}
|
||||
wait_for_push_order() {
|
||||
var result = super.wait_for_push_order(...arguments);
|
||||
result = Boolean(result || this.pos.is_french_country());
|
||||
return result;
|
||||
}
|
||||
_get_qr_code_data() {
|
||||
if (this.pos.is_french_country()){
|
||||
return false;
|
||||
} else {
|
||||
return super._get_qr_code_data(...arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
Registries.Model.extend(Order, L10nFrOrder);
|
||||
|
||||
|
||||
const L10nFrOrderline = (Orderline) => class L10nFrOrderline extends Orderline {
|
||||
can_be_merged_with(orderline) {
|
||||
if (this.pos.is_french_country()) {
|
||||
const order = this.pos.get_order();
|
||||
const lastOrderline = order.orderlines.at(order.orderlines.length - 1);
|
||||
if ((lastOrderline.product.id !== orderline.product.id || lastOrderline.quantity < 0)) {
|
||||
return false;
|
||||
}
|
||||
return super.can_be_merged_with(...arguments);
|
||||
}
|
||||
return super.can_be_merged_with(...arguments);
|
||||
}
|
||||
}
|
||||
Registries.Model.extend(Orderline, L10nFrOrderline);
|
||||
|
||||
});
|
||||
|
|
@ -1,22 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('pos-receipt-order-data')]" position="inside">
|
||||
<t t-if="receipt.l10n_fr_hash !== false">
|
||||
<br/>
|
||||
<div style="word-wrap:break-word;"><t t-esc="receipt.l10n_fr_hash"/></div>
|
||||
<t t-name="l10n_fr_pos_cert.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('before-footer')]" position="inside">
|
||||
<t t-if="order.l10n_fr_hash !== false">
|
||||
<div class="text-break pt-3"><t t-out="order.l10n_fr_hash"/></div>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="OrderLinesReceipt" t-inherit="point_of_sale.OrderLinesReceipt" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//t[@t-foreach='receipt.orderlines']" position="inside">
|
||||
<t t-if="receipt.l10n_fr_hash !== false and line.price_manually_set">
|
||||
<t t-name="l10n_fr_pos_cert.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension">
|
||||
<xpath expr="//Orderline" position="inside">
|
||||
<t t-if="line.order_id.l10n_fr_hash !== false and line.price_type === 'manual' and !props.basic_receipt">
|
||||
<div class="pos-receipt-right-padding">
|
||||
Old unit price:
|
||||
<span class="oldPrice">
|
||||
<s>
|
||||
<t t-esc="env.pos.format_currency(line.taxed_lst_unit_price, 'Product Price')" /> / Units
|
||||
<t t-out="line.product_id.displayPriceUnit" /> / Units
|
||||
</s>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="l10n_fr_pos_cert.OrderSummary" t-inherit="point_of_sale.OrderSummary" t-inherit-mode="extension">
|
||||
<xpath expr="//Orderline" position="inside" >
|
||||
<t t-if="pos.is_french_country() !== false and line.price_type === 'manual'">
|
||||
<li class="info">
|
||||
Old unit price:
|
||||
<span class="oldPrice">
|
||||
<s>
|
||||
<t t-esc="line.product_id.displayPriceUnit" /> / Units
|
||||
</s>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//ul[hasclass('info-list')]" position="inside">
|
||||
<t t-if="env.pos.is_french_country() !== false and props.line.price_manually_set">
|
||||
<li class="info">
|
||||
Old unit price:
|
||||
<span class="oldPrice">
|
||||
<s>
|
||||
<t t-esc="env.pos.format_currency(props.line.get_taxed_lst_unit_price(),'Product Price')" /> / Units
|
||||
</s>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import * as Chrome from "@point_of_sale/../tests/pos/tours/utils/chrome_util";
|
||||
import * as Dialog from "@point_of_sale/../tests/generic_helpers/dialog_util";
|
||||
import * as ReceiptScreen from "@point_of_sale/../tests/pos/tours/utils/receipt_screen_util";
|
||||
import * as PaymentScreen from "@point_of_sale/../tests/pos/tours/utils/payment_screen_util";
|
||||
import * as ProductScreen from "@point_of_sale/../tests/pos/tours/utils/product_screen_util";
|
||||
import * as Numpad from "@point_of_sale/../tests/generic_helpers/numpad_util";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
registry.category("web_tour.tours").add("l10nFrPosCertSelfInvoicingTour", {
|
||||
steps: () =>
|
||||
[
|
||||
Chrome.startPoS(),
|
||||
Dialog.confirm("Open Register"),
|
||||
ProductScreen.clickDisplayedProduct("Desk Pad"),
|
||||
ProductScreen.clickPayButton(),
|
||||
PaymentScreen.clickPaymentMethod("Cash"),
|
||||
PaymentScreen.clickValidate(),
|
||||
ReceiptScreen.isShown(),
|
||||
{
|
||||
trigger: ".pos-receipt #posqrcode",
|
||||
content: "QR code is visible on the receipt",
|
||||
},
|
||||
].flat(),
|
||||
});
|
||||
|
||||
registry.category("web_tour.tours").add("test_correct_old_price_upon_price_change_fr", {
|
||||
steps: () =>
|
||||
[
|
||||
Chrome.startPoS(),
|
||||
Dialog.confirm("Open Register"),
|
||||
ProductScreen.clickDisplayedProduct("Desk Pad"),
|
||||
ProductScreen.selectedOrderlineHas("Desk Pad", "1", "1.98"),
|
||||
Numpad.click("Price"),
|
||||
Numpad.isActive("Price"),
|
||||
Numpad.click("5"),
|
||||
ProductScreen.selectedOrderlineHas("Desk Pad", "1", "5.00"),
|
||||
{
|
||||
content: "Old unit price is correctly shown",
|
||||
trigger: ".order-container .orderline.selected:has(.oldPrice:contains(1.98))",
|
||||
},
|
||||
ProductScreen.clickPayButton(),
|
||||
PaymentScreen.clickPaymentMethod("Cash"),
|
||||
PaymentScreen.clickValidate(),
|
||||
ReceiptScreen.isShown(),
|
||||
{
|
||||
content: "Old unit price is correctly shown",
|
||||
trigger: ".order-container .orderline:has(.oldPrice:contains(1.98))",
|
||||
},
|
||||
].flat(),
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { PosConfig } from "@point_of_sale/../tests/unit/data/pos_config.data";
|
||||
|
||||
PosConfig._records = PosConfig._records.map((record) => ({
|
||||
...record,
|
||||
company_id: 251,
|
||||
}));
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { ResCompany } from "@point_of_sale/../tests/unit/data/res_company.data";
|
||||
|
||||
ResCompany._records = [
|
||||
...ResCompany._records,
|
||||
{
|
||||
id: 251,
|
||||
currency_id: 125,
|
||||
email: false,
|
||||
website: false,
|
||||
company_registry: false,
|
||||
vat: false,
|
||||
name: "My FR Company",
|
||||
phone: "",
|
||||
partner_id: 1,
|
||||
country_id: 75,
|
||||
state_id: false,
|
||||
tax_calculation_rounding_method: "round_per_line",
|
||||
point_of_sale_use_ticket_qr_code: true,
|
||||
point_of_sale_ticket_unique_code: false,
|
||||
point_of_sale_ticket_portal_url_display_mode: "qr_code_and_url",
|
||||
street: "",
|
||||
city: "",
|
||||
zip: "",
|
||||
account_fiscal_country_id: 75,
|
||||
},
|
||||
];
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { ResCountry } from "@point_of_sale/../tests/unit/data/res_country.data";
|
||||
|
||||
ResCountry._records = [
|
||||
...ResCountry._records,
|
||||
{
|
||||
id: 75,
|
||||
name: "France",
|
||||
code: "FR",
|
||||
vat_label: "VAT",
|
||||
},
|
||||
];
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { test, expect } from "@odoo/hoot";
|
||||
import { setupPosEnv } from "@point_of_sale/../tests/unit/utils";
|
||||
import { definePosModels } from "@point_of_sale/../tests/unit/data/generate_model_definitions";
|
||||
|
||||
definePosModels();
|
||||
|
||||
test("canEditPayment", async () => {
|
||||
const store = await setupPosEnv();
|
||||
store.addNewOrder();
|
||||
const order = store.getOrder();
|
||||
// In FR localisation, edit payment should not be visble even when order.nb_print === 0
|
||||
expect(store.canEditPayment(order)).toBe(false);
|
||||
});
|
||||
|
|
@ -1 +1,6 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_frontend
|
||||
from . import test_string_to_hash
|
||||
from . import test_fr_pos
|
||||
from . import test_hash
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from odoo.addons.account_edi.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.addons.point_of_sale.tests.test_generic_localization import TestGenericLocalization
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'post_install_l10n')
|
||||
class TestGenericFR(TestGenericLocalization):
|
||||
@classmethod
|
||||
@AccountTestInvoicingCommon.setup_country('fr')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.addons.point_of_sale.tests.test_frontend import TestPointOfSaleHttpCommon
|
||||
|
||||
|
||||
class Testl10nFrPosCert(TestPointOfSaleHttpCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
company = cls.main_pos_config.company_id
|
||||
company.country_id = cls.env.ref("base.fr")
|
||||
company.point_of_sale_use_ticket_qr_code = True
|
||||
company.point_of_sale_ticket_portal_url_display_mode = 'qr_code_and_url'
|
||||
|
||||
|
||||
@tagged("post_install_l10n", "post_install", "-at_install")
|
||||
class TestUi(Testl10nFrPosCert):
|
||||
def test_pos_use_ticket_qr_code_for_fr(self):
|
||||
company = self.main_pos_config.company_id
|
||||
self.assertEqual(company.country_id.code, "FR", "Company should be set to France (FR)")
|
||||
self.main_pos_config.with_user(self.pos_user).open_ui()
|
||||
self.start_pos_tour("l10nFrPosCertSelfInvoicingTour", login="pos_user")
|
||||
|
||||
def test_correct_old_price_upon_price_change_fr(self):
|
||||
company = self.main_pos_config.company_id
|
||||
self.assertEqual(company.country_id.code, "FR", "Company should be set to France (FR)")
|
||||
self.main_pos_config.with_user(self.pos_user).open_ui()
|
||||
self.start_pos_tour("test_correct_old_price_upon_price_change_fr", login="pos_user")
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
from odoo.addons.point_of_sale.tests.common import CommonPosTest
|
||||
from odoo.addons.account_edi.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
from odoo import fields
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestHash(CommonPosTest):
|
||||
|
||||
@classmethod
|
||||
@AccountTestInvoicingCommon.setup_country('fr')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
def test_hashes_should_be_equal_if_no_alteration(self):
|
||||
product1 = self.env['product.product'].create({
|
||||
'name': 'product1',
|
||||
})
|
||||
|
||||
self.pos_config_usd.open_ui()
|
||||
pos_session = self.pos_config_usd.current_session_id
|
||||
draft_order = {
|
||||
'access_token': False,
|
||||
'amount_paid': 0,
|
||||
'amount_return': 0,
|
||||
'amount_tax': 0,
|
||||
'amount_total': 0,
|
||||
'date_order': fields.Datetime.to_string(fields.Datetime.now()),
|
||||
'lines': [],
|
||||
'name': '/',
|
||||
'partner_id': False,
|
||||
'session_id': pos_session.id,
|
||||
'sequence_number': 2,
|
||||
'payment_ids': [],
|
||||
'uuid': '12345-123-1234',
|
||||
'last_order_preparation_change': '{}',
|
||||
'user_id': self.env.uid,
|
||||
'state': 'draft',
|
||||
}
|
||||
|
||||
self.env['pos.order'].sync_from_ui([draft_order])
|
||||
self.env.invalidate_all()
|
||||
|
||||
paid_order = {
|
||||
'access_token': False,
|
||||
'amount_paid': 20,
|
||||
'amount_return': -5.0,
|
||||
'amount_tax': 0,
|
||||
'amount_total': 15.0,
|
||||
'date_order': fields.Datetime.to_string(fields.Datetime.now()),
|
||||
'lines': [[0,
|
||||
0,
|
||||
{'discount': 0,
|
||||
'pack_lot_ids': [],
|
||||
'price_unit': 15.0,
|
||||
'product_id': product1.id,
|
||||
'price_subtotal': 15.0,
|
||||
'price_subtotal_incl': 15.0,
|
||||
'qty': 1,
|
||||
'tax_ids': []}]],
|
||||
'name': 'Order 12345-123-1234',
|
||||
'partner_id': False,
|
||||
'session_id': pos_session.id,
|
||||
'sequence_number': 2,
|
||||
'payment_ids': [[0,
|
||||
0,
|
||||
{'amount': 20.0,
|
||||
'name': fields.Datetime.now(),
|
||||
'payment_method_id': self.cash_payment_method.id}]],
|
||||
'uuid': '12345-123-1234',
|
||||
'last_order_preparation_change': '{}',
|
||||
'user_id': self.env.uid,
|
||||
'state': 'paid',
|
||||
}
|
||||
|
||||
self.env['pos.order'].sync_from_ui([paid_order])
|
||||
self.env.invalidate_all()
|
||||
|
||||
posted_order = self.env['pos.order'].search([('uuid', '=', '12345-123-1234')])
|
||||
self.assertEqual(posted_order.state, 'paid')
|
||||
|
||||
self.pos_config_usd.current_session_id.action_pos_session_closing_control()
|
||||
|
||||
self.assertEqual(posted_order.l10n_fr_hash, posted_order._compute_hash(''))
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon
|
||||
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
|
||||
from odoo.tests import tagged
|
||||
from ..models.pos import ORDER_FIELDS, LINE_FIELDS
|
||||
from ..models.pos import ORDER_FIELDS_BEFORE_17_4, ORDER_FIELDS_FROM_17_4, LINE_FIELDS
|
||||
from json import dumps
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestStringToHash(TestPointOfSaleCommon):
|
||||
class TestStringToHash(TestPoSCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref="l10n_fr.l10n_fr_pcg_chart_template"):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.pricelist = cls.env['product.pricelist'].create({
|
||||
'name': 'Test Pricelist',
|
||||
|
|
@ -28,7 +28,11 @@ class TestStringToHash(TestPointOfSaleCommon):
|
|||
|
||||
for order in orders:
|
||||
values = {}
|
||||
for field in ORDER_FIELDS:
|
||||
if order.pos_version:
|
||||
order_fields = ORDER_FIELDS_FROM_17_4
|
||||
else:
|
||||
order_fields = ORDER_FIELDS_BEFORE_17_4
|
||||
for field in order_fields:
|
||||
values[field] = _getattrstring(order, field)
|
||||
|
||||
for line in order.lines:
|
||||
|
|
@ -74,7 +78,7 @@ class TestStringToHash(TestPointOfSaleCommon):
|
|||
order = self.env['pos.order'].create({
|
||||
'company_id': self.company_data['company'].id,
|
||||
'partner_id': self.partner_a.id,
|
||||
'session_id': self.pos_config.current_session_id.id,
|
||||
'session_id': self.basic_config.current_session_id.id,
|
||||
'lines': lines,
|
||||
'amount_total': currency.round(total_amount + total_tax),
|
||||
'amount_tax': currency.round(total_tax),
|
||||
|
|
@ -96,15 +100,15 @@ class TestStringToHash(TestPointOfSaleCommon):
|
|||
return order
|
||||
|
||||
def test_string_to_hash(self):
|
||||
self.pos_config.open_ui()
|
||||
self.basic_config.open_ui()
|
||||
order = self._create_and_pay_pos_order([
|
||||
{'qty': 1, 'price_unit': 10000, 'product': self.product_a, 'tax_ids': self.tax_sale_a},
|
||||
{'qty': 2, 'price_unit': 5000, 'product': self.product_a, 'tax_ids': self.tax_sale_b},
|
||||
{'qty': 3, 'price_unit': 2000, 'tax_ids': self.tax_sale_b | self.tax_sale_b}
|
||||
], [
|
||||
{'amount': 10000, 'payment_method': self.bank_payment_method},
|
||||
{'amount': 1200, 'payment_method': self.cash_payment_method},
|
||||
{'amount': 20000, 'payment_method': self.credit_payment_method}
|
||||
{'amount': 10000, 'payment_method': self.bank_pm1},
|
||||
{'amount': 8900, 'payment_method': self.cash_pm1},
|
||||
{'amount': 11000, 'payment_method': self.pay_later_pm}
|
||||
])
|
||||
self.pos_config.current_session_id.action_pos_session_closing_control()
|
||||
self.basic_config.current_session_id.action_pos_session_closing_control()
|
||||
self.assertEqual(order.l10n_fr_string_to_hash, self._compute_string_to_hash_original(order))
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
def migrate(cr, version):
|
||||
cr.execute("""
|
||||
UPDATE ir_sequence iseq
|
||||
SET implementation = 'no_gap'
|
||||
FROM pos_config pconfig,res_company rcomp, res_country rcount, res_partner rpart
|
||||
WHERE rcount.code in ('FR', 'MF', 'MQ', 'NC', 'PF', 'RE', 'GF', 'GP', 'TF', 'BL', 'PM', 'YT', 'WF')
|
||||
AND rpart.country_id = rcount.id
|
||||
AND rcomp.partner_id = rpart.id
|
||||
AND pconfig.company_id = rcomp.id
|
||||
AND (pconfig.sequence_id = iseq.id or pconfig.sequence_line_id = iseq.id)
|
||||
AND iseq.implementation = 'standard'
|
||||
""")
|
||||
|
|
@ -3,16 +3,16 @@
|
|||
<field name="name">Sales Closings</field>
|
||||
<field name="model">account.sale.closing</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree create="false" import="false">
|
||||
<list create="false" import="false">
|
||||
<field name="date_closing_start"/>
|
||||
<field name="date_closing_stop"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="currency_id" column_invisible="True"/>
|
||||
<field name="frequency"/>
|
||||
<field name="sequence_number" groups="base.group_no_one"/>
|
||||
<field name="total_interval"/>
|
||||
<field name="cumulative_total"/>
|
||||
</tree>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
@ -63,5 +63,5 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_list_view_account_sale_closing" id="menu_account_closing_reporting" parent="l10n_fr.account_reports_fr_statements_menu" sequence="90"/>
|
||||
<menuitem action="action_list_view_account_sale_closing" id="menu_account_closing_reporting" parent="l10n_fr_account.account_reports_fr_statements_menu" sequence="90"/>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<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()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
<xpath expr="//field[@name='pos_reference']" position='after'>
|
||||
<field string='Hash' name="l10n_fr_hash" groups="base.group_no_one"/>
|
||||
</xpath>
|
||||
<field name="payment_ids" position="attributes">
|
||||
<attribute name="readonly" add="country_code in ['FR', 'MF', 'MQ', 'NC', 'PF', 'RE', 'GF', 'GP', 'TF']" separator=" or " />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@
|
|||
<form position="inside">
|
||||
<field name="country_code" invisible="1"/>
|
||||
</form>
|
||||
<xpath expr="//field[@name='point_of_sale_use_ticket_qr_code']/.." position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('country_code', 'in', ('FR', 'MF', 'MQ', 'NC', 'PF', 'RE', 'GF', 'GP', 'TF'))]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//label[@for='point_of_sale_use_ticket_qr_code']/.." position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('country_code', 'in', ('FR', 'MF', 'MQ', 'NC', 'PF', 'RE', 'GF', 'GP', 'TF'))]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
[project]
|
||||
name = "odoo-bringout-oca-ocb-l10n_fr_pos_cert"
|
||||
version = "16.0.0"
|
||||
description = "France - VAT Anti-Fraud Certification for Point of Sale (CGI 286 I-3 bis) - Odoo addon"
|
||||
description = "France - VAT Anti-Fraud Certification for Point of Sale (CGI 286 I-3 bis) -
|
||||
Odoo addon
|
||||
"
|
||||
authors = [
|
||||
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||
]
|
||||
dependencies = [
|
||||
"odoo-bringout-oca-ocb-l10n_fr>=16.0.0",
|
||||
"odoo-bringout-oca-ocb-point_of_sale>=16.0.0",
|
||||
"TODO_MAP-l10n_fr_account>=19.0.0",
|
||||
"odoo-bringout-oca-ocb-point_of_sale>=19.0.0",
|
||||
"requests>=2.25.1"
|
||||
]
|
||||
readme = "README.md"
|
||||
|
|
@ -17,7 +19,7 @@ classifiers = [
|
|||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Topic :: Office/Business",
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue