mirror of
https://github.com/bringout/oca-ocb-l10n_europe.git
synced 2026-04-27 05:02:02 +02:00
Initial commit: L10N_Europe packages
This commit is contained in:
commit
9803722600
2377 changed files with 380711 additions and 0 deletions
60
odoo-bringout-oca-ocb-l10n_fr_pos_cert/README.md
Normal file
60
odoo-bringout-oca-ocb-l10n_fr_pos_cert/README.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# France - VAT Anti-Fraud Certification for Point of Sale (CGI 286 I-3 bis)
|
||||
|
||||
|
||||
This add-on brings the technical requirements of the French regulation CGI art. 286, I. 3° bis that stipulates certain criteria concerning the inalterability, security, storage and archiving of data related to sales to private individuals (B2C).
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Install it if you use the Point of Sale app to sell to individuals.
|
||||
|
||||
The module adds following features:
|
||||
|
||||
Inalterability: deactivation of all the ways to cancel or modify key data of POS orders, invoices and journal entries
|
||||
|
||||
Security: chaining algorithm to verify the inalterability
|
||||
|
||||
Storage: automatic sales closings with computation of both period and cumulative totals (daily, monthly, annually)
|
||||
|
||||
Access to download the mandatory Certificate of Conformity delivered by Odoo SA (only for Odoo Enterprise users)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install odoo-bringout-oca-ocb-l10n_fr_pos_cert
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
This addon depends on:
|
||||
- l10n_fr
|
||||
- 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`.
|
||||
|
||||
## 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
|
||||
32
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/ARCHITECTURE.md
Normal file
32
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||
V --> C[Controllers]
|
||||
V --> W[Wizards – Transient Models]
|
||||
C --> M[Models and ORM]
|
||||
W --> M
|
||||
M --> R[Reports]
|
||||
DX[Data XML] --> M
|
||||
S[Security – ACLs and Groups] -. enforces .-> M
|
||||
|
||||
subgraph L10n_fr_pos_cert Module - l10n_fr_pos_cert
|
||||
direction LR
|
||||
M:::layer
|
||||
W:::layer
|
||||
C:::layer
|
||||
V:::layer
|
||||
R:::layer
|
||||
S:::layer
|
||||
DX:::layer
|
||||
end
|
||||
|
||||
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||
```
|
||||
|
||||
Notes
|
||||
- Views include tree/form/kanban templates and report templates.
|
||||
- Controllers provide website/portal routes when present.
|
||||
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||
- Data XML loads data/demo records; Security defines groups and access.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Configuration
|
||||
|
||||
Refer to Odoo settings for l10n_fr_pos_cert. Configure related models, access rights, and options as needed.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Controllers
|
||||
|
||||
This module does not define custom HTTP controllers.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Dependencies
|
||||
|
||||
This addon depends on:
|
||||
|
||||
- [l10n_fr](../../odoo-bringout-oca-ocb-l10n_fr)
|
||||
- [point_of_sale](../../odoo-bringout-oca-ocb-point_of_sale)
|
||||
4
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# FAQ
|
||||
|
||||
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||
- Q: How to enable? A: Start server with --addon l10n_fr_pos_cert or install in UI.
|
||||
7
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/INSTALL.md
Normal file
7
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Install
|
||||
|
||||
```bash
|
||||
pip install odoo-bringout-oca-ocb-l10n_fr_pos_cert"
|
||||
# or
|
||||
uv pip install odoo-bringout-oca-ocb-l10n_fr_pos_cert"
|
||||
```
|
||||
18
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/MODELS.md
Normal file
18
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Models
|
||||
|
||||
Detected core models and extensions in l10n_fr_pos_cert.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class account_sale_closing
|
||||
class account_fiscal_position
|
||||
class pos_config
|
||||
class pos_order
|
||||
class pos_order_line
|
||||
class pos_session
|
||||
class res_company
|
||||
```
|
||||
|
||||
Notes
|
||||
- Classes show model technical names; fields omitted for brevity.
|
||||
- Items listed under _inherit are extensions of existing models.
|
||||
6
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/OVERVIEW.md
Normal file
6
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/OVERVIEW.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Overview
|
||||
|
||||
Packaged Odoo addon: l10n_fr_pos_cert. Provides features documented in upstream Odoo 16 under this addon.
|
||||
|
||||
- Source: OCA/OCB 16.0, addon l10n_fr_pos_cert
|
||||
- License: LGPL-3
|
||||
26
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/REPORTS.md
Normal file
26
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/REPORTS.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Reports
|
||||
|
||||
Report definitions and templates in l10n_fr_pos_cert.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class ReportPosHashIntegrity
|
||||
AbstractModel <|-- ReportPosHashIntegrity
|
||||
```
|
||||
|
||||
## Available Reports
|
||||
|
||||
No named reports found in XML files.
|
||||
|
||||
|
||||
## Report Files
|
||||
|
||||
- **__init__.py** (Python logic)
|
||||
- **pos_hash_integrity.py** (Python logic)
|
||||
- **pos_hash_integrity.xml** (XML template/definition)
|
||||
|
||||
## Notes
|
||||
- Named reports above are accessible through Odoo's reporting menu
|
||||
- Python files define report logic and data processing
|
||||
- XML files contain report templates, definitions, and formatting
|
||||
- Reports are integrated with Odoo's printing and email systems
|
||||
41
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/SECURITY.md
Normal file
41
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Security
|
||||
|
||||
Access control and security definitions in l10n_fr_pos_cert.
|
||||
|
||||
## Access Control Lists (ACLs)
|
||||
|
||||
Model access permissions defined in:
|
||||
- **[ir.model.access.csv](../l10n_fr_pos_cert/security/ir.model.access.csv)**
|
||||
- 1 model access rules
|
||||
|
||||
## Record Rules
|
||||
|
||||
Row-level security rules defined in:
|
||||
|
||||
## Security Groups & Configuration
|
||||
|
||||
Security groups and permissions defined in:
|
||||
- **[account_closing_intercompany.xml](../l10n_fr_pos_cert/security/account_closing_intercompany.xml)**
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Security Layers"
|
||||
A[Users] --> B[Groups]
|
||||
B --> C[Access Control Lists]
|
||||
C --> D[Models]
|
||||
B --> E[Record Rules]
|
||||
E --> F[Individual Records]
|
||||
end
|
||||
```
|
||||
|
||||
Security files overview:
|
||||
- **[account_closing_intercompany.xml](../l10n_fr_pos_cert/security/account_closing_intercompany.xml)**
|
||||
- Security groups, categories, and XML-based rules
|
||||
- **[ir.model.access.csv](../l10n_fr_pos_cert/security/ir.model.access.csv)**
|
||||
- Model access permissions (CRUD rights)
|
||||
|
||||
Notes
|
||||
- Access Control Lists define which groups can access which models
|
||||
- Record Rules provide row-level security (filter records by user/group)
|
||||
- Security groups organize users and define permission sets
|
||||
- All security is enforced at the ORM level by Odoo
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Troubleshooting
|
||||
|
||||
- Ensure Python and Odoo environment matches repo guidance.
|
||||
- Check database connectivity and logs if startup fails.
|
||||
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||
7
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Usage
|
||||
|
||||
Start Odoo including this addon (from repo root):
|
||||
|
||||
```bash
|
||||
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon l10n_fr_pos_cert
|
||||
```
|
||||
3
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/WIZARDS.md
Normal file
3
odoo-bringout-oca-ocb-l10n_fr_pos_cert/doc/WIZARDS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Wizards
|
||||
|
||||
This module does not include UI wizards.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import report
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
|
||||
def _setup_inalterability(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
# enable ping for this module
|
||||
env['publisher_warranty.contract'].update_notification(cron_mode=True)
|
||||
|
||||
fr_companies = env['res.company'].search([('partner_id.country_id.code', 'in', env['res.company']._get_unalterable_country())])
|
||||
if fr_companies:
|
||||
fr_companies._create_secure_sequence(['l10n_fr_pos_cert_sequence_id'])
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'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': """
|
||||
This add-on brings the technical requirements of the French regulation CGI art. 286, I. 3° bis that stipulates certain criteria concerning the inalterability, security, storage and archiving of data related to sales to private individuals (B2C).
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Install it if you use the Point of Sale app to sell to individuals.
|
||||
|
||||
The module adds following features:
|
||||
|
||||
Inalterability: deactivation of all the ways to cancel or modify key data of POS orders, invoices and journal entries
|
||||
|
||||
Security: chaining algorithm to verify the inalterability
|
||||
|
||||
Storage: automatic sales closings with computation of both period and cumulative totals (daily, monthly, annually)
|
||||
|
||||
Access to download the mandatory Certificate of Conformity delivered by Odoo SA (only for Odoo Enterprise users)
|
||||
""",
|
||||
'depends': ['l10n_fr', 'point_of_sale'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'data': [
|
||||
'views/pos_views.xml',
|
||||
'views/account_sale_closure.xml',
|
||||
'views/pos_inalterability_menuitem.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'report/pos_hash_integrity.xml',
|
||||
'data/account_sale_closure_cron.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/account_closing_intercompany.xml',
|
||||
],
|
||||
'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/**/*',
|
||||
],
|
||||
},
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<odoo>
|
||||
<record model="ir.cron" id="account_sale_closing_daily">
|
||||
<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>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="account_sale_closing_monthly">
|
||||
<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>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="account_sale_closing_annually">
|
||||
<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>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,590 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * l10n_fr_pos_cert
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.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"
|
||||
"Last-Translator: Manon Rondou <ronm@odoo.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#. 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
|
||||
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."
|
||||
|
||||
#. 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."
|
||||
msgstr ""
|
||||
"Selon la loi française, vous ne pouvez pas modifier une ligne de commande de "
|
||||
"caisse. 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. "
|
||||
"Forbidden fields: %s."
|
||||
msgstr ""
|
||||
"Selon la loi française, vous ne pouvez pas modifier une commande de caisse. "
|
||||
"Champs interdits : %s."
|
||||
|
||||
#. 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"
|
||||
|
||||
#. 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."
|
||||
msgstr ""
|
||||
"La comptabilité n'est pas inaltérable pour l'entreprise %s. Ce mécanisme est "
|
||||
"conçu pour les entreprises dont la comptabilité est inaltérable."
|
||||
|
||||
#. 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 ""
|
||||
"Une erreur s'est produite lors de la vérification de l'inaltérabilité. "
|
||||
"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"
|
||||
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:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Closing Date"
|
||||
msgstr "Date de clôture"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__frequency
|
||||
msgid "Closing Type"
|
||||
msgstr "Type de clôture"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Sociétés"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__company_id
|
||||
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 "Contrôle des données du point de vente"
|
||||
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."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Créé par"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__create_date
|
||||
msgid "Created on"
|
||||
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é"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__currency_id
|
||||
msgid "Currency"
|
||||
msgstr "Devise"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__daily
|
||||
msgid "Daily"
|
||||
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"
|
||||
|
||||
#. 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"
|
||||
msgstr "Date à partir de laquelle le total de l'intervalle est calculé"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Date to which the values are computed"
|
||||
msgstr "Date jusqu'à laquelle les valeurs sont calculées"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nom affiché"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
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"
|
||||
msgstr "Position fiscale"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.pos_fr_statements_menu
|
||||
msgid "French Statements"
|
||||
msgstr "Relevés français"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__name
|
||||
msgid "Frequency and unique sequence number"
|
||||
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"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_report_l10n_fr_pos_cert_report_pos_hash_integrity
|
||||
msgid "Get french pos hash integrity result as PDF."
|
||||
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.pos_order_form_inherit
|
||||
msgid "Hash"
|
||||
msgstr "Hachage"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.report,name:l10n_fr_pos_cert.action_report_pos_hash_integrity
|
||||
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_sale_closing__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_hash
|
||||
msgid "Inalteralbility Hash"
|
||||
msgstr "Hachage d'inaltérabilité"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_secure_sequence_number
|
||||
msgid "Inalteralbility No Gap Sequence #"
|
||||
msgstr "Numéro de séquence sans écart pour inaltérabilité"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__l10n_fr_pos_cert_sequence_id
|
||||
msgid "L10N Fr Pos Cert Sequence"
|
||||
msgstr "Séquence pour la caisse certifiée française"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_string_to_hash
|
||||
msgid "L10N Fr String To Hash"
|
||||
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."
|
||||
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."
|
||||
|
||||
#. 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"
|
||||
|
||||
#. 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"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__last_order_hash
|
||||
msgid "Last Order entry's inalteralbility hash"
|
||||
msgstr "Hachage d'inaltérabilité sur la dernière commande"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__last_order_id
|
||||
msgid "Last Pos Order"
|
||||
msgstr "Dernière commande de caisse"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__last_order_id
|
||||
msgid "Last Pos order included in the grand total"
|
||||
msgstr "Dernière commande de caisse incluse dans le grand total"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Dernière mise à jour par"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Dernière mise à jour le"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
msgid "Missing Country"
|
||||
msgstr "Pays manquant"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__monthly
|
||||
msgid "Monthly"
|
||||
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"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__name
|
||||
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
|
||||
msgid "Old unit price:"
|
||||
msgstr "Anciennement:"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.action_check_pos_hash_integrity
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_check_move_integrity_reporting
|
||||
msgid "POS Inalterability Check"
|
||||
msgstr "Vérification d'inaltérabilité de la caisse"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__total_interval
|
||||
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"
|
||||
msgstr "Paramétrage du point de vente"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_order_line
|
||||
msgid "Point of Sale Order Lines"
|
||||
msgstr "Lignes des commandes du point de vente"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_order
|
||||
msgid "Point of Sale Orders"
|
||||
msgstr "Commandes du point de vente"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_session
|
||||
msgid "Point of Sale Session"
|
||||
msgstr "Session 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 "Résultat du test d'intégrité -"
|
||||
msgstr "Résultat du test d’intégrité -"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_account_sale_closing
|
||||
msgid "Sale Closing"
|
||||
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."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.act_window,name:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_account_closing
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_account_closing_reporting
|
||||
msgid "Sales Closings"
|
||||
msgstr "Clôtures de ventes"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.actions.act_window,help:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
msgid ""
|
||||
"Sales closings run automatically on a daily, monthly and annual basis. It "
|
||||
"computes both period and cumulative totals from all the sales entries posted "
|
||||
"in the system after the previous closing."
|
||||
msgstr ""
|
||||
"Les clôtures de ventes sont exécutées automatiquement de manière "
|
||||
"journalière, mensuelle et annuelle. Des totaux cumulatifs et par période "
|
||||
"sont calculés à partir de toutes les entrées comptabilisées dans le système "
|
||||
"depuis la clôture précédente."
|
||||
|
||||
#. 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"
|
||||
" <br/>\n"
|
||||
" <br/>\n"
|
||||
" Ces conditions sont respectées via "
|
||||
"une fonction de hachage des ventes du Point de Vente.\n"
|
||||
" <br/>\n"
|
||||
" <br/>"
|
||||
msgstr ""
|
||||
"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"
|
||||
" <br/>\n"
|
||||
" <br/>"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__sequence_number
|
||||
msgid "Sequence #"
|
||||
msgstr "Numéro de séquence"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_start
|
||||
msgid "Starting Date"
|
||||
msgstr "Date de début"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.actions.act_window,help:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
msgid "The closings are created by Odoo"
|
||||
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
|
||||
msgid "The company %s doesn't have a country set."
|
||||
msgstr "La société %s n'a pas de pays configuré."
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__currency_id
|
||||
msgid "The company's currency"
|
||||
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 "
|
||||
"installation of the module France - Certification CGI 286 I-3 bis. - POS"
|
||||
msgstr ""
|
||||
"Il n'y a pas encore de commande marquée pour l'inaltérabilité des données "
|
||||
"pour la société %s. Cette fonction n'est utilisée que pour les commandes "
|
||||
"générées après l'installation du module France - Certification CGI 286 I-3 "
|
||||
"bis. - POS"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__total_interval
|
||||
msgid ""
|
||||
"Total in receivable accounts during the interval, excluding overlapping "
|
||||
"periods"
|
||||
msgstr ""
|
||||
"Total sur des comptes débiteurs pendant l'intervalle, en excluant les "
|
||||
"périodes qui se superposent"
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__cumulative_total
|
||||
msgid "Total in receivable accounts since the beginnig of times"
|
||||
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."
|
||||
msgstr ""
|
||||
"Toutes les ventes effectuées via le Point de Vente\n"
|
||||
" sont bien dans la chaîne de "
|
||||
"hachage."
|
||||
|
||||
#. 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"
|
||||
|
||||
#. 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."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas modifer une position fiscale utilisée dans une commande "
|
||||
"de caisse. Vous pouvez l'archiver et en créer une nouvelle."
|
||||
|
||||
#. 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."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas modifier les valeurs assurant de l'inaltérabilité de la "
|
||||
"caisse."
|
||||
|
||||
#. 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."
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * l10n_fr_pos_cert
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.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"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: l10n_fr_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
|
||||
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."
|
||||
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."
|
||||
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 ""
|
||||
|
||||
#. 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."
|
||||
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"
|
||||
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:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Closing Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__frequency
|
||||
msgid "Closing Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__company_id
|
||||
msgid "Company"
|
||||
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"
|
||||
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 ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. 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 ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__currency_id
|
||||
msgid "Currency"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__daily
|
||||
msgid "Daily"
|
||||
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:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__date_closing_start
|
||||
msgid "Date from which the total interval is computed"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__date_closing_stop
|
||||
msgid "Date to which the values are computed"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.pos_fr_statements_menu
|
||||
msgid "French Statements"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__name
|
||||
msgid "Frequency and unique sequence number"
|
||||
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 ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_report_l10n_fr_pos_cert_report_pos_hash_integrity
|
||||
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.pos_order_form_inherit
|
||||
msgid "Hash"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.report,name:l10n_fr_pos_cert.action_report_pos_hash_integrity
|
||||
msgid "Hash integrity result PDF"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_hash
|
||||
msgid "Inalteralbility Hash"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_secure_sequence_number
|
||||
msgid "Inalteralbility No Gap Sequence #"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_res_company__l10n_fr_pos_cert_sequence_id
|
||||
msgid "L10N Fr Pos Cert Sequence"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_pos_order__l10n_fr_string_to_hash
|
||||
msgid "L10N Fr String To Hash"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.ui.view,arch_db:l10n_fr_pos_cert.report_pos_hash_integrity
|
||||
msgid "Last Entry"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__last_order_hash
|
||||
msgid "Last Order entry's inalteralbility hash"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__last_order_id
|
||||
msgid "Last Pos Order"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__last_order_id
|
||||
msgid "Last Pos order included in the grand total"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
msgid "Missing Country"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields.selection,name:l10n_fr_pos_cert.selection__account_sale_closing__frequency__monthly
|
||||
msgid "Monthly"
|
||||
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 ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__name
|
||||
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
|
||||
msgid "Old unit price:"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.server,name:l10n_fr_pos_cert.action_check_pos_hash_integrity
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_check_move_integrity_reporting
|
||||
msgid "POS Inalterability Check"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__total_interval
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_order_line
|
||||
msgid "Point of Sale Order Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_order
|
||||
msgid "Point of Sale Orders"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_pos_session
|
||||
msgid "Point of Sale Session"
|
||||
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é -"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model,name:l10n_fr_pos_cert.model_account_sale_closing
|
||||
msgid "Sale Closing"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.actions.act_window,name:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_account_closing
|
||||
#: model:ir.ui.menu,name:l10n_fr_pos_cert.menu_account_closing_reporting
|
||||
msgid "Sales Closings"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.actions.act_window,help:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
msgid ""
|
||||
"Sales closings run automatically on a daily, monthly and annual basis. It "
|
||||
"computes both period and cumulative totals from all the sales entries posted"
|
||||
" in the system after the previous closing."
|
||||
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"
|
||||
" <br/>\n"
|
||||
" <br/>\n"
|
||||
" Ces conditions sont respectées via une fonction de hachage des ventes du Point de Vente.\n"
|
||||
" <br/>\n"
|
||||
" <br/>"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__sequence_number
|
||||
msgid "Sequence #"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,field_description:l10n_fr_pos_cert.field_account_sale_closing__date_closing_start
|
||||
msgid "Starting Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model_terms:ir.actions.act_window,help:l10n_fr_pos_cert.action_list_view_account_sale_closing
|
||||
msgid "The closings are created by Odoo"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#. odoo-javascript
|
||||
#: code:addons/l10n_fr_pos_cert/static/src/js/pos.js:0
|
||||
#, python-format
|
||||
msgid "The company %s doesn't have a country set."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__currency_id
|
||||
msgid "The company's currency"
|
||||
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 "
|
||||
"installation of the module France - Certification CGI 286 I-3 bis. - POS"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__total_interval
|
||||
msgid ""
|
||||
"Total in receivable accounts during the interval, excluding overlapping "
|
||||
"periods"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_fr_pos_cert
|
||||
#: model:ir.model.fields,help:l10n_fr_pos_cert.field_account_sale_closing__cumulative_total
|
||||
msgid "Total in receivable accounts since the beginnig of times"
|
||||
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."
|
||||
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"
|
||||
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."
|
||||
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."
|
||||
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 ""
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import account_fiscal_position
|
||||
from . import res_company
|
||||
from . import pos
|
||||
from . import account_closing
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
# -*- 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 dateutil.relativedelta import relativedelta
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv.expression import AND
|
||||
|
||||
|
||||
class AccountClosing(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
|
||||
It takes its earliest brother to infer from when the computation needs to be done
|
||||
in order to compute its own data.
|
||||
"""
|
||||
_name = 'account.sale.closing'
|
||||
_order = 'date_closing_stop desc, sequence_number desc'
|
||||
_description = "Sale Closing"
|
||||
|
||||
name = fields.Char(help="Frequency and unique sequence number", required=True)
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True, required=True)
|
||||
date_closing_stop = fields.Datetime(string="Closing Date", help='Date to which the values are computed', readonly=True, required=True)
|
||||
date_closing_start = fields.Datetime(string="Starting Date", help='Date from which the total interval is computed', readonly=True, required=True)
|
||||
frequency = fields.Selection(string='Closing Type', selection=[('daily', 'Daily'), ('monthly', 'Monthly'), ('annually', 'Annual')], readonly=True, required=True)
|
||||
total_interval = fields.Monetary(string="Period Total", help='Total in receivable accounts during the interval, excluding overlapping periods', readonly=True, required=True)
|
||||
cumulative_total = fields.Monetary(string="Cumulative Grand Total", help='Total in receivable accounts since the beginnig of times', readonly=True, required=True)
|
||||
sequence_number = fields.Integer('Sequence #', readonly=True, required=True)
|
||||
last_order_id = fields.Many2one('pos.order', string='Last Pos Order', help='Last Pos order included in the grand total', readonly=True)
|
||||
last_order_hash = fields.Char(string='Last Order entry\'s inalteralbility hash', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', help="The company's currency", readonly=True, related='company_id.currency_id', store=True)
|
||||
|
||||
def _query_for_aml(self, company, first_move_sequence_number, date_start):
|
||||
params = {'company_id': company.id}
|
||||
query = '''WITH aggregate AS (SELECT m.id AS move_id,
|
||||
aml.balance AS balance,
|
||||
aml.id as line_id
|
||||
FROM account_move_line aml
|
||||
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
|
||||
WHERE j.type = 'sale'
|
||||
AND aml.company_id = %(company_id)s
|
||||
AND m.state = 'posted'
|
||||
AND acc.account_type = 'asset_receivable' '''
|
||||
|
||||
if first_move_sequence_number is not False and first_move_sequence_number is not None:
|
||||
params['first_move_sequence_number'] = first_move_sequence_number
|
||||
query += '''AND m.secure_sequence_number > %(first_move_sequence_number)s'''
|
||||
elif date_start:
|
||||
#the first time we compute the closing, we consider only from the installation of the module
|
||||
params['date_start'] = date_start
|
||||
query += '''AND m.date >= %(date_start)s'''
|
||||
|
||||
query += " ORDER BY m.secure_sequence_number DESC) "
|
||||
query += '''SELECT array_agg(move_id) AS move_ids,
|
||||
array_agg(line_id) AS line_ids,
|
||||
sum(balance) AS balance
|
||||
FROM aggregate'''
|
||||
|
||||
self.env.cr.execute(query, params)
|
||||
return self.env.cr.dictfetchall()[0]
|
||||
|
||||
def _compute_amounts(self, frequency, company):
|
||||
"""
|
||||
Method used to compute all the business data of the new object.
|
||||
It will search for previous closings of the same frequency to infer the move from which
|
||||
account move lines should be fetched.
|
||||
@param {string} frequency: a valid value of the selection field on the object (daily, monthly, annually)
|
||||
frequencies are literal (daily means 24 hours and so on)
|
||||
@param {recordset} company: the company for which the closing is done
|
||||
@return {dict} containing {field: value} for each business field of the object
|
||||
"""
|
||||
interval_dates = self._interval_dates(frequency, company)
|
||||
previous_closing = self.search([
|
||||
('frequency', '=', frequency),
|
||||
('company_id', '=', company.id)], limit=1, order='sequence_number desc')
|
||||
|
||||
first_order = self.env['pos.order']
|
||||
date_start = interval_dates['interval_from']
|
||||
cumulative_total = 0
|
||||
if previous_closing:
|
||||
first_order = previous_closing.last_order_id
|
||||
date_start = previous_closing.create_date
|
||||
cumulative_total += previous_closing.cumulative_total
|
||||
|
||||
domain = [('company_id', '=', company.id), ('state', 'in', ('paid', 'done', 'invoiced'))]
|
||||
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)]])
|
||||
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)]])
|
||||
|
||||
orders = self.env['pos.order'].search(domain, order='date_order desc')
|
||||
|
||||
total_interval = sum(orders.mapped('amount_total'))
|
||||
cumulative_total += total_interval
|
||||
|
||||
# We keep the reference to avoid gaps (like daily object during the weekend)
|
||||
last_order = first_order
|
||||
if orders:
|
||||
last_order = orders[0]
|
||||
|
||||
return {'total_interval': total_interval,
|
||||
'cumulative_total': cumulative_total,
|
||||
'last_order_id': last_order.id,
|
||||
'last_order_hash': last_order.l10n_fr_secure_sequence_number,
|
||||
'date_closing_stop': interval_dates['date_stop'],
|
||||
'date_closing_start': date_start,
|
||||
'name': interval_dates['name_interval'] + ' - ' + interval_dates['date_stop'][:10]}
|
||||
|
||||
def _interval_dates(self, frequency, company):
|
||||
"""
|
||||
Method used to compute the theoretical date from which account move lines should be fetched
|
||||
@param {string} frequency: a valid value of the selection field on the object (daily, monthly, annually)
|
||||
frequencies are literal (daily means 24 hours and so on)
|
||||
@param {recordset} company: the company for which the closing is done
|
||||
@return {dict} the theoretical date from which account move lines are fetched.
|
||||
date_stop date to which the move lines are fetched, always now()
|
||||
the dates are in their Odoo Database string representation
|
||||
"""
|
||||
date_stop = datetime.utcnow()
|
||||
interval_from = None
|
||||
name_interval = ''
|
||||
if frequency == 'daily':
|
||||
interval_from = date_stop - timedelta(days=1)
|
||||
name_interval = _('Daily Closing')
|
||||
elif frequency == 'monthly':
|
||||
interval_from = date_stop - relativedelta(months=1)
|
||||
name_interval = _('Monthly Closing')
|
||||
elif frequency == 'annually':
|
||||
interval_from = date_stop - relativedelta(years=1)
|
||||
name_interval = _('Annual Closing')
|
||||
|
||||
return {'interval_from': FieldDateTime.to_string(interval_from),
|
||||
'date_stop': FieldDateTime.to_string(date_stop),
|
||||
'name_interval': name_interval}
|
||||
|
||||
def write(self, vals):
|
||||
raise UserError(_('Sale Closings are not meant to be written or deleted under any circumstances.'))
|
||||
|
||||
@api.ondelete(at_uninstall=True)
|
||||
def _unlink_never(self):
|
||||
raise UserError(_('Sale Closings are not meant to be written or deleted under any circumstances.'))
|
||||
|
||||
@api.model
|
||||
def _automated_closing(self, frequency='daily'):
|
||||
"""To be executed by the CRON to create an object of the given frequency for each company that needs it
|
||||
@param {string} frequency: a valid value of the selection field on the object (daily, monthly, annually)
|
||||
frequencies are literal (daily means 24 hours and so on)
|
||||
@return {recordset} all the objects created for the given frequency
|
||||
"""
|
||||
res_company = self.env['res.company'].search([])
|
||||
account_closings = self.env['account.sale.closing']
|
||||
for company in res_company.filtered(lambda c: c._is_accounting_unalterable()):
|
||||
new_sequence_number = company.l10n_fr_closing_sequence_id.next_by_id()
|
||||
values = self._compute_amounts(frequency, company)
|
||||
values['frequency'] = frequency
|
||||
values['company_id'] = company.id
|
||||
values['sequence_number'] = new_sequence_number
|
||||
account_closings |= account_closings.create(values)
|
||||
|
||||
return account_closings
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import _, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountFiscalPosition(models.Model):
|
||||
_inherit = "account.fiscal.position"
|
||||
|
||||
def write(self, vals):
|
||||
if "tax_ids" in vals:
|
||||
if self.env["pos.order"].sudo().search_count([("fiscal_position_id", "in", self.ids)]):
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot modify a fiscal position used in a POS order. "
|
||||
"You should archive it and create a new one."
|
||||
)
|
||||
)
|
||||
return super(AccountFiscalPosition, self).write(vals)
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
# -*- 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 collections import defaultdict
|
||||
|
||||
|
||||
class pos_config(models.Model):
|
||||
_inherit = 'pos.config'
|
||||
|
||||
def open_ui(self):
|
||||
for config in self:
|
||||
if not config.company_id.country_id:
|
||||
raise UserError(_("You have to set a country in your company setting."))
|
||||
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()
|
||||
|
||||
|
||||
class pos_session(models.Model):
|
||||
_inherit = 'pos.session'
|
||||
|
||||
def _check_session_timing(self):
|
||||
self.ensure_one()
|
||||
return True
|
||||
|
||||
def open_frontend_cb(self):
|
||||
sessions_to_check = self.filtered(lambda s: s.config_id.company_id._is_accounting_unalterable())
|
||||
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()
|
||||
|
||||
|
||||
ORDER_FIELDS = ['date_order', 'user_id', 'lines', 'payment_ids', 'pricelist_id', 'partner_id', 'session_id', 'pos_reference', 'sale_journal', 'fiscal_position_id']
|
||||
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):
|
||||
_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)
|
||||
|
||||
def _get_new_hash(self, secure_seq_number):
|
||||
""" 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'')
|
||||
|
||||
def _compute_hash(self, previous_hash):
|
||||
""" Computes the hash of the browse_record given as self, based on the hash
|
||||
of the previous record in the company's securisation sequence given as parameter"""
|
||||
self.ensure_one()
|
||||
hash_string = sha256((previous_hash + self.l10n_fr_string_to_hash).encode('utf-8'))
|
||||
return hash_string.hexdigest()
|
||||
|
||||
def _compute_string_to_hash(self):
|
||||
def _getattrstring(field_value, field_type, model_name=None):
|
||||
if field_type in ('many2many', 'one2many'):
|
||||
if field_value:
|
||||
sorted_ids = sorted_relational_ids.get(model_name, [])
|
||||
value_set = set(field_value)
|
||||
field_value = [id for id in sorted_ids if id in value_set]
|
||||
else:
|
||||
field_value = []
|
||||
return str(field_value)
|
||||
|
||||
def collect_sorted_relational_ids(orders_data, lines_data, order_field_defs, line_field_defs):
|
||||
relational_ids = defaultdict(set)
|
||||
|
||||
for data_list, field_names, field_defs in (
|
||||
(orders_data, ORDER_FIELDS, order_field_defs),
|
||||
(lines_data, LINE_FIELDS, line_field_defs),
|
||||
):
|
||||
for record in data_list:
|
||||
for field in field_names:
|
||||
field_def = field_defs.get(field)
|
||||
if field_def and field_def['type'] in ('many2many', 'one2many'):
|
||||
ids = record.get(field) or []
|
||||
relational_ids[field_def['comodel']].update(ids)
|
||||
|
||||
sorted_relational_ids = {}
|
||||
for model_name, ids in relational_ids.items():
|
||||
if ids:
|
||||
# Use search() to get IDs sorted by _order the same way Odoo ORM does for relational fields
|
||||
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='')
|
||||
lines_data = self.lines.read(LINE_FIELDS + ['id', 'order_id'], load='')
|
||||
|
||||
orders_by_id = {order['id']: order for order in orders_data}
|
||||
lines_by_order = defaultdict(list)
|
||||
for line in lines_data:
|
||||
lines_by_order[line['order_id']].append(line)
|
||||
order_field_defs = {
|
||||
field: {
|
||||
'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
|
||||
}
|
||||
line_field_defs = {
|
||||
field: {
|
||||
'type': self.lines._fields[field].type,
|
||||
'comodel': self.lines._fields[field].comodel_name if hasattr(self.lines._fields[field], 'comodel_name') else None
|
||||
}
|
||||
for field in LINE_FIELDS
|
||||
}
|
||||
|
||||
sorted_relational_ids = collect_sorted_relational_ids(orders_data, lines_data, order_field_defs, line_field_defs)
|
||||
|
||||
for order in self:
|
||||
values = {}
|
||||
order_data = orders_by_id[order.id]
|
||||
|
||||
for field in ORDER_FIELDS:
|
||||
field_def = order_field_defs[field]
|
||||
values[field] = _getattrstring(order_data.get(field), field_def['type'], field_def['comodel'])
|
||||
|
||||
for line in lines_by_order[order.id]:
|
||||
for field in LINE_FIELDS:
|
||||
k = 'line_%d_%s' % (line['id'], field)
|
||||
field_def = line_field_defs[field]
|
||||
values[k] = _getattrstring(line.get(field), field_def['type'], field_def['comodel'])
|
||||
|
||||
#make the json serialization canonical
|
||||
# (https://tools.ietf.org/html/draft-staykov-hu-json-canonical-form-00)
|
||||
order.l10n_fr_string_to_hash = dumps(values, sort_keys=True,
|
||||
ensure_ascii=True, indent=None,
|
||||
separators=(',',':'))
|
||||
|
||||
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']:
|
||||
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)):
|
||||
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)
|
||||
# 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)
|
||||
return res
|
||||
|
||||
@api.ondelete(at_uninstall=True)
|
||||
def _unlink_except_pos_so(self):
|
||||
for order in self:
|
||||
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"
|
||||
|
||||
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):
|
||||
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)
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, api, fields, _
|
||||
from odoo.exceptions import UserError
|
||||
from datetime import datetime
|
||||
from odoo.fields import Datetime, Date
|
||||
from odoo.tools.misc import format_date
|
||||
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')
|
||||
timestamp = Datetime.from_string(record[field])
|
||||
if ctx.get('lang'):
|
||||
res_lang = record.env['res.lang']._lang_get(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)
|
||||
return Datetime.context_timestamp(record, timestamp)
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
l10n_fr_pos_cert_sequence_id = fields.Many2one('ir.sequence')
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
companies = super().create(vals_list)
|
||||
for company in companies:
|
||||
#when creating a new french company, create the securisation sequence as well
|
||||
if company._is_accounting_unalterable():
|
||||
sequence_fields = ['l10n_fr_pos_cert_sequence_id']
|
||||
company._create_secure_sequence(sequence_fields)
|
||||
return companies
|
||||
|
||||
def write(self, vals):
|
||||
res = super(ResCompany, self).write(vals)
|
||||
#if country changed to fr, create the securisation sequence
|
||||
for company in self:
|
||||
if company._is_accounting_unalterable():
|
||||
sequence_fields = ['l10n_fr_pos_cert_sequence_id']
|
||||
company._create_secure_sequence(sequence_fields)
|
||||
return res
|
||||
|
||||
def _action_check_pos_hash_integrity(self):
|
||||
return self.env.ref('l10n_fr_pos_cert.action_report_pos_hash_integrity').report_action(self.id)
|
||||
|
||||
def _check_pos_hash_integrity(self):
|
||||
"""Checks that all posted or invoiced pos orders have still the same data as when they were posted
|
||||
and raises an error with the result.
|
||||
"""
|
||||
def build_order_info(order):
|
||||
entry_reference = _('(Receipt ref.: %s)')
|
||||
order_reference_string = order.pos_reference and entry_reference % order.pos_reference or ''
|
||||
return [ctx_tz(order, 'date_order'), order.l10n_fr_hash, order.name, order_reference_string, ctx_tz(order, 'write_date')]
|
||||
|
||||
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),
|
||||
('l10n_fr_secure_sequence_number', '!=', 0)], order="l10n_fr_secure_sequence_number ASC")
|
||||
|
||||
if not orders:
|
||||
msg_alert = (_('There isn\'t any order flagged for data inalterability yet for the company %s. This mechanism only runs for point of sale orders generated after the installation of the module France - Certification CGI 286 I-3 bis. - POS', self.env.company.name))
|
||||
raise UserError(msg_alert)
|
||||
|
||||
previous_hash = u''
|
||||
corrupted_orders = []
|
||||
for order in orders:
|
||||
if order.l10n_fr_hash != order._compute_hash(previous_hash=previous_hash):
|
||||
corrupted_orders.append(order.name)
|
||||
msg_alert = (_('Corrupted data on point of sale order with id %s.', order.id))
|
||||
previous_hash = order.l10n_fr_hash
|
||||
orders.invalidate_recordset()
|
||||
|
||||
orders_sorted_date = orders.sorted(lambda o: o.date_order)
|
||||
start_order_info = build_order_info(orders_sorted_date[0])
|
||||
end_order_info = build_order_info(orders_sorted_date[-1])
|
||||
|
||||
report_dict.update({
|
||||
'first_order_name': start_order_info[2],
|
||||
'first_order_hash': start_order_info[1],
|
||||
'first_order_date': start_order_info[0],
|
||||
'last_order_name': end_order_info[2],
|
||||
'last_order_hash': end_order_info[1],
|
||||
'last_order_date': end_order_info[0],
|
||||
})
|
||||
corrupted_orders = ', '.join([o for o in corrupted_orders])
|
||||
return {
|
||||
'result': report_dict or 'None',
|
||||
'msg_alert': msg_alert or 'None',
|
||||
'printing_date': format_date(self.env, Date.to_string( Date.today())),
|
||||
'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)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import pos_hash_integrity
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ReportPosHashIntegrity(models.AbstractModel):
|
||||
_name = 'report.l10n_fr_pos_cert.report_pos_hash_integrity'
|
||||
_description = 'Get french pos hash integrity result as PDF.'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
data = data or {}
|
||||
data.update(self.env.company._check_pos_hash_integrity() or {})
|
||||
return {
|
||||
'doc_ids' : docids,
|
||||
'doc_model' : self.env['res.company'],
|
||||
'data' : data,
|
||||
'docs' : self.env['res.company'].browse(self.env.company.id),
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="report_pos_hash_integrity">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="company">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<div class="row" id="hash_header">
|
||||
<div class="col-12">
|
||||
<br/>
|
||||
<h2>Résultat du test d'intégrité - <span t-esc="data['printing_date']"/></h2>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" id="hash_config_review">
|
||||
<h6>
|
||||
Selon l’article 286 du code général des impôts français, toute livraison de bien ou prestation
|
||||
de services ne donnant pas lieu à facturation et étant enregistrée au moyen d’un logiciel ou
|
||||
d’un système de caisse doit satisfaire à des conditions d’inaltérabilité et de sécurisation des
|
||||
données en vue d’un contrôle de l’administration fiscale.
|
||||
<br/>
|
||||
<br/>
|
||||
Ces conditions sont respectées via une fonction de hachage des ventes du Point de Vente.
|
||||
<br/>
|
||||
<br/>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="data['result'] != 'None'">
|
||||
<div class="row">
|
||||
<div class="col-12" id="hash_data_consistency">
|
||||
<br/>
|
||||
<h3>Contrôle des données du point de vente</h3>
|
||||
<br/>
|
||||
<t t-if="data['result'] != 'None' and data['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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" id="hash_data_consistency_table">
|
||||
<table class="table table-bordered" style="table-layout: fixed">
|
||||
<thead style="display: table-row-group">
|
||||
<tr>
|
||||
<th class="text-center" style="width: 25%" scope="col">First Hash</th>
|
||||
<th class="text-center" style="width: 25%" scope="col">First Entry</th>
|
||||
<th class="text-center" style="width: 25%" scope="col">Last Hash</th>
|
||||
<th class="text-center" style="width: 25%" scope="col">Last Entry</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-if="data['result'] != 'None'">
|
||||
<t t-if="data['result']['first_order_hash'] != 'None'">
|
||||
<tr>
|
||||
<td><span t-esc="data['result']['first_order_hash']"/></td>
|
||||
<td>
|
||||
<span t-esc="data['result']['first_order_name']"/> <br/>
|
||||
<span t-esc="data['result']['first_order_date']"/>
|
||||
</td>
|
||||
<td><span t-esc="data['result']['last_order_hash']"/></td>
|
||||
<td>
|
||||
<span t-esc="data['result']['last_order_name']"/> <br/>
|
||||
<span t-esc="data['result']['last_order_date']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<t t-if="data['corrupted_orders'] != 'None'">
|
||||
<h5>
|
||||
Données corrompues sur la commande du point de vente:
|
||||
</h5>
|
||||
<span t-esc="data['corrupted_orders']"/> <br/>
|
||||
</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>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<odoo noupdate="1">
|
||||
|
||||
<record model="ir.rule" id="account_sale_closing_multi_company">
|
||||
<field name="name">Sale Closing multi-company</field>
|
||||
<field name="model_id" ref="model_account_sale_closing"/>
|
||||
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_l10n_fr_pos_cert_account_sale_closing_user,l10n_fr_pos_cert.account.sale.closing.user,l10n_fr_pos_cert.model_account_sale_closing,base.group_user,1,0,0,0
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.oldPrice {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
odoo.define('l10n_fr_pos_cert.Chrome', function (require) {
|
||||
'use strict';
|
||||
|
||||
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.'),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(Chrome, PosFrCertChrome);
|
||||
|
||||
return Chrome;
|
||||
});
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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;
|
||||
});
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
odoo.define('l10n_fr_pos_cert.ClosePosPopup', function (require) {
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
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;
|
||||
});
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
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);
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?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>
|
||||
</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">
|
||||
<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
|
||||
</s>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?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 @@
|
|||
from . import test_string_to_hash
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon
|
||||
from odoo.tests import tagged
|
||||
from ..models.pos import ORDER_FIELDS, LINE_FIELDS
|
||||
from json import dumps
|
||||
|
||||
|
||||
@tagged('post_install_l10n', 'post_install', '-at_install')
|
||||
class TestStringToHash(TestPointOfSaleCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref="l10n_fr.l10n_fr_pcg_chart_template"):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.pricelist = cls.env['product.pricelist'].create({
|
||||
'name': 'Test Pricelist',
|
||||
'currency_id': cls.company_data['company'].currency_id.id,
|
||||
})
|
||||
cls.company.country_id = cls.env.company.account_fiscal_country_id.id
|
||||
|
||||
def _compute_string_to_hash_original(self, orders):
|
||||
def _getattrstring(obj, field_str):
|
||||
field_value = obj[field_str]
|
||||
if obj._fields[field_str].type == 'many2one':
|
||||
field_value = field_value.id
|
||||
if obj._fields[field_str].type in ['many2many', 'one2many']:
|
||||
field_value = field_value.sorted().ids
|
||||
return str(field_value)
|
||||
|
||||
for order in orders:
|
||||
values = {}
|
||||
for field in ORDER_FIELDS:
|
||||
values[field] = _getattrstring(order, field)
|
||||
|
||||
for line in order.lines:
|
||||
for field in LINE_FIELDS:
|
||||
k = 'line_%d_%s' % (line.id, field)
|
||||
values[k] = _getattrstring(line, field)
|
||||
# make the json serialization canonical
|
||||
# (https://tools.ietf.org/html/draft-staykov-hu-json-canonical-form-00)
|
||||
return dumps(values, sort_keys=True,
|
||||
ensure_ascii=True, indent=None,
|
||||
separators=(',', ':'))
|
||||
|
||||
def _create_and_pay_pos_order(self, line_data_list, payments):
|
||||
currency = self.company_data['company'].currency_id
|
||||
lines = []
|
||||
total_tax = 0.0
|
||||
total_amount = 0.0
|
||||
|
||||
for idx, line_data in enumerate(line_data_list):
|
||||
product = line_data.get('product', self.product_a)
|
||||
qty = line_data['qty']
|
||||
price_unit = line_data['price_unit']
|
||||
taxes = line_data.get('tax_ids', self.tax_sale_a)
|
||||
|
||||
line_tax = sum((tax.amount / 100) * qty * price_unit for tax in taxes)
|
||||
line_total = qty * price_unit + line_tax
|
||||
|
||||
total_tax += line_tax
|
||||
total_amount += qty * price_unit
|
||||
|
||||
rounded_total = currency.round(line_total)
|
||||
|
||||
lines.append((0, 0, {
|
||||
'name': f"OL/000{idx + 1}",
|
||||
'product_id': product.id,
|
||||
'price_unit': price_unit,
|
||||
'qty': qty,
|
||||
'tax_ids': [(6, 0, taxes.ids)],
|
||||
'price_subtotal': qty * price_unit,
|
||||
'price_subtotal_incl': rounded_total,
|
||||
}))
|
||||
|
||||
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,
|
||||
'lines': lines,
|
||||
'amount_total': currency.round(total_amount + total_tax),
|
||||
'amount_tax': currency.round(total_tax),
|
||||
'amount_paid': 0,
|
||||
'amount_return': 0,
|
||||
'pricelist_id': self.pricelist.id
|
||||
})
|
||||
|
||||
for payment in payments:
|
||||
context_payment = {
|
||||
"active_ids": [order.id],
|
||||
"active_id": order.id
|
||||
}
|
||||
pos_make_payment = self.env['pos.make.payment'].with_context(context_payment).create({
|
||||
'amount': payment['amount'],
|
||||
'payment_method_id': payment['payment_method'].id,
|
||||
})
|
||||
pos_make_payment.with_context(context_payment).check()
|
||||
return order
|
||||
|
||||
def test_string_to_hash(self):
|
||||
self.pos_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}
|
||||
])
|
||||
self.pos_config.current_session_id.action_pos_session_closing_control()
|
||||
self.assertEqual(order.l10n_fr_string_to_hash, self._compute_string_to_hash_original(order))
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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'
|
||||
""")
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<odoo>
|
||||
<record id="list_view_account_sale_closing" model="ir.ui.view">
|
||||
<field name="name">Sales Closings</field>
|
||||
<field name="model">account.sale.closing</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree 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="frequency"/>
|
||||
<field name="sequence_number" groups="base.group_no_one"/>
|
||||
<field name="total_interval"/>
|
||||
<field name="cumulative_total"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="form_view_account_sale_closing" model="ir.ui.view">
|
||||
<field name="name">Sales Closings</field>
|
||||
<field name="model">account.sale.closing</field>
|
||||
<field name="arch" type="xml">
|
||||
<form create="false" edit="false" string="Account Closing">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="date_closing_start"/>
|
||||
<field name="date_closing_stop"/>
|
||||
<field name="frequency"/>
|
||||
<field name="sequence_number" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="total_interval"/>
|
||||
<field name="cumulative_total"/>
|
||||
<field name="last_order_id" groups="account.group_account_readonly"/>
|
||||
<field name="last_order_hash" groups="account.group_account_readonly"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_list_view_account_sale_closing" model="ir.actions.act_window">
|
||||
<field name="name">Sales Closings</field>
|
||||
<field name="res_model">account.sale.closing</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_nocreate">
|
||||
The closings are created by Odoo
|
||||
</p><p>
|
||||
Sales closings run automatically on a daily, monthly and annual basis. It computes both period and cumulative totals from all the sales entries posted in the system after the previous closing.
|
||||
</p>
|
||||
</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"/>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<odoo>
|
||||
<record id="action_report_pos_hash_integrity" model="ir.actions.report">
|
||||
<field name="name">Hash integrity result PDF</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_fr_pos_cert.report_pos_hash_integrity</field>
|
||||
<field name="report_file">l10n_fr_pos_cert.report_pos_hash_integrity</field>
|
||||
</record>
|
||||
<record model="ir.actions.server" id="action_check_pos_hash_integrity">
|
||||
<field name="name">POS Inalterability Check</field>
|
||||
<field name="model_id" ref="account.model_res_company"/>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = env.company._action_check_pos_hash_integrity()
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="pos_fr_statements_menu" name="French Statements" parent="point_of_sale.menu_point_rep" sequence="9" />
|
||||
<menuitem action="l10n_fr_pos_cert.action_list_view_account_sale_closing" id="menu_account_closing" parent="pos_fr_statements_menu" sequence="80"/>
|
||||
<menuitem action="l10n_fr_pos_cert.action_check_pos_hash_integrity" id="menu_check_move_integrity_reporting" parent="pos_fr_statements_menu" sequence="90"/>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<odoo>
|
||||
<record id="pos_order_form_inherit" model="ir.ui.view">
|
||||
<field name="name">pos.order.form.inherit</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='pos_reference']" position='after'>
|
||||
<field string='Hash' name="l10n_fr_hash" groups="base.group_no_one"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.l10n_fr_pos_cert</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="point_of_sale.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<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>
|
||||
43
odoo-bringout-oca-ocb-l10n_fr_pos_cert/pyproject.toml
Normal file
43
odoo-bringout-oca-ocb-l10n_fr_pos_cert/pyproject.toml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[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"
|
||||
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",
|
||||
"requests>=2.25.1"
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">= 3.11"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"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.12",
|
||||
"Topic :: Office/Business",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/bringout/0"
|
||||
repository = "https://github.com/bringout/0"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.metadata]
|
||||
allow-direct-references = true
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["l10n_fr_pos_cert"]
|
||||
|
||||
[tool.rye]
|
||||
managed = true
|
||||
dev-dependencies = [
|
||||
"pytest>=8.4.1",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue