Initial commit: L10N_Europe packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:52 +02:00
commit 9803722600
2377 changed files with 380711 additions and 0 deletions

View file

@ -0,0 +1,60 @@
# Spain - SII EDI Suministro de Libros
This module sends the taxes information (mostly VAT) of the
vendor bills and customer invoices to the SII. It is called
Procedimiento G417 - IVA. Llevanza de libros registro. It is
required for every company with a turnover of +6M€ and others can
already make use of it. The invoices are automatically
sent after validation.
How the information is sent to the SII depends on the
configuration that is put in the taxes. The taxes
that were in the chart template (l10n_es) are automatically
configured to have the right type. It is possible however
that extra taxes need to be created for certain exempt/no sujeta reasons.
You need to configure your certificate and the tax agency.
## Installation
```bash
pip install odoo-bringout-oca-ocb-l10n_es_edi_sii
```
## Dependencies
This addon depends on:
- l10n_es
- account_edi
## Manifest Information
- **Name**: Spain - SII EDI Suministro de Libros
- **Version**: 1.0
- **Category**: Accounting/Localizations/EDI
- **License**: LGPL-3
- **Installable**: False
## Source
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `l10n_es_edi_sii`.
## 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

View 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_es_edi_sii Module - l10n_es_edi_sii
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.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for l10n_es_edi_sii. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- [l10n_es](../../odoo-bringout-oca-ocb-l10n_es)
- [account_edi](../../odoo-bringout-oca-ocb-account_edi)

View 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_es_edi_sii or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ocb-l10n_es_edi_sii"
# or
uv pip install odoo-bringout-oca-ocb-l10n_es_edi_sii"
```

View file

@ -0,0 +1,20 @@
# Models
Detected core models and extensions in l10n_es_edi_sii.
```mermaid
classDiagram
class account_tax
class account_tax_template
class l10n_es_edi_certificate
class l10n_es_sii_account_tax_mixin
class account_edi_format
class account_move
class mail_template
class res_company
class res_config_settings
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: l10n_es_edi_sii. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon l10n_es_edi_sii
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,41 @@
# Security
Access control and security definitions in l10n_es_edi_sii.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[ir.model.access.csv](../l10n_es_edi_sii/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:
- **[l10n_es_edi_certificate.xml](../l10n_es_edi_sii/security/l10n_es_edi_certificate.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:
- **[ir.model.access.csv](../l10n_es_edi_sii/security/ir.model.access.csv)**
- Model access permissions (CRUD rights)
- **[l10n_es_edi_certificate.xml](../l10n_es_edi_sii/security/l10n_es_edi_certificate.xml)**
- Security groups, categories, and XML-based rules
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

View file

@ -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.

View 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_es_edi_sii
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizards
from odoo import api, SUPERUSER_ID
def _l10n_es_edi_post_init(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
companies = env['res.company'].search([('partner_id.country_id.code', '=', 'ES')])
all_chart_templates = companies.chart_template_id
current_chart_template = all_chart_templates
while current_chart_template.parent_id:
all_chart_templates |= current_chart_template.parent_id
current_chart_template = current_chart_template.parent_id
if all_chart_templates:
tax_templates = env['account.tax.template'].search([
('chart_template_id', 'in', all_chart_templates.ids),
'|', '|', '|',
('l10n_es_type', '!=', False),
('l10n_es_exempt_reason', '!=', False),
('tax_scope', '!=', False),
('l10n_es_bien_inversion', '!=', False),
])
xml_ids = tax_templates.get_external_id()
for company in companies:
for tax_template in tax_templates:
module, xml_id = xml_ids.get(tax_template.id).split('.')
tax = env.ref('%s.%s_%s' % (module, company.id, xml_id), raise_if_not_found=False)
if tax:
tax.write({
'l10n_es_exempt_reason': tax_template.l10n_es_exempt_reason,
'tax_scope': tax_template.tax_scope,
'l10n_es_type': tax_template.l10n_es_type,
'l10n_es_bien_inversion': tax_template.l10n_es_bien_inversion,
})

View file

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# Thanks to AEOdoo and the Spanish community
# Specially among others Ignacio Ibeas, Pedro Baeza and Landoo
{
'name': "Spain - SII EDI Suministro de Libros",
'version': '1.0',
'category': 'Accounting/Localizations/EDI',
'description': """
This module sends the taxes information (mostly VAT) of the
vendor bills and customer invoices to the SII. It is called
Procedimiento G417 - IVA. Llevanza de libros registro. It is
required for every company with a turnover of +6M and others can
already make use of it. The invoices are automatically
sent after validation.
How the information is sent to the SII depends on the
configuration that is put in the taxes. The taxes
that were in the chart template (l10n_es) are automatically
configured to have the right type. It is possible however
that extra taxes need to be created for certain exempt/no sujeta reasons.
You need to configure your certificate and the tax agency.
""",
'depends': [
'l10n_es',
'account_edi',
],
'data': [
'data/account_tax_data.xml',
'data/account_edi_data.xml',
'data/res_partner_data.xml',
'security/ir.model.access.csv',
'security/l10n_es_edi_certificate.xml',
'views/account_tax_views.xml',
'views/l10n_es_edi_certificate_views.xml',
'views/res_config_settings_views.xml',
'views/account_move_views.xml',
],
'demo': ['demo/demo_certificate.xml'],
'external_dependencies': {
'python': ['pyOpenSSL'],
},
'post_init_hook': '_l10n_es_edi_post_init',
'license': 'LGPL-3',
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="edi_es_sii" model="account.edi.format">
<field name="name">SII IVA Llevanza de libros registro (ES)</field>
<field name="code">es_sii</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,446 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="l10n_es.account_tax_template_s_iva21b" model="account.tax.template">
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva21s" model="account.tax.template">
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_s_iva21isp" model="account.tax.template">
<field name="l10n_es_type">sujeto</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_bc" model="account.tax.template">
<field name="name">21% IVA soportado (bienes corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_sc" model="account.tax.template">
<field name="name">21% IVA soportado (servicios corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_sp_in" model="account.tax.template">
<field name="name">IVA 21% Adquisición de servicios intracomunitarios</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_ic_bc" model="account.tax.template">
<field name="name">IVA 21% Adquisición Intracomunitaria. Bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_ic_bi" model="account.tax.template">
<field name="name">IVA 21% Adquisición Intracomunitaria. Bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_ibc" model="account.tax.template">
<field name="name">IVA 21% Importaciones bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_ibi" model="account.tax.template">
<field name="name">IVA 21% Importaciones bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf21td" model="account.tax.template">
<field name="name">Retenciones IRPF (Trabajadores) dinerarios</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_sp_ex" model="account.tax.template">
<field name="name">IVA 4% Adquisición de servicios extracomunitarios</field>
<field name="l10n_es_type">sujeto_isp</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_sp_ex" model="account.tax.template">
<field name="name">IVA 10% Adquisición de servicios extracomunitarios</field>
<field name="l10n_es_type">sujeto_isp</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_sp_ex" model="account.tax.template">
<field name="name">IVA 21% Adquisición de servicios extracomunitarios</field>
<field name="l10n_es_type">sujeto_isp</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_ic_bc" model="account.tax.template">
<field name="name">IVA 4% Adquisición Intracomunitario. Bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_ic_bi" model="account.tax.template">
<field name="name">IVA 4% Adquisición Intracomunitario. Bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_ic_bc" model="account.tax.template">
<field name="name">IVA 10% Adquisición Intracomunitario. Bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_ic_bi" model="account.tax.template">
<field name="name">IVA 10% Adquisición Intracomunitario. Bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0_sp_i" model="account.tax.template">
<field name="name">IVA 0% Prestación de servicios intracomunitario</field>
<field name="l10n_es_type">no_sujeto_loc</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_s_iva_ns" model="account.tax.template">
<field name="name">No sujeto Repercutido (Servicios)</field>
<field name="l10n_es_type">no_sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_s_iva_ns_b" model="account.tax.template">
<field name="name">No sujeto Repercutido (Bienes)</field>
<field name="l10n_es_type">no_sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva_e" model="account.tax.template">
<field name="name">IVA 0% Prestación de servicios extracomunitaria</field>
<field name="l10n_es_type">no_sujeto_loc</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_ibc" model="account.tax.template">
<field name="name">IVA 4% Importaciones bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_ibi" model="account.tax.template">
<field name="name">IVA 4% Importaciones bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_ibc" model="account.tax.template">
<field name="name">IVA 10% Importaciones bienes corrientes</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_ibi" model="account.tax.template">
<field name="name">IVA 10% Importaciones bienes de inversión</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_bi" model="account.tax.template">
<field name="name">4% IVA Soportado (bienes de inversión)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
<field name="l10n_es_bien_inversion">True</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_sc" model="account.tax.template">
<field name="name">4% IVA soportado (servicios corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_bi" model="account.tax.template">
<field name="name">10% IVA Soportado (bienes de inversión)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
<field name="l10n_es_bien_inversion">True</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_bi" model="account.tax.template">
<field name="name">21% IVA Soportado (bienes de inversión)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
<field name="l10n_es_bien_inversion">True</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_bc" model="account.tax.template">
<field name="name">10% IVA soportado (bienes corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_bc" model="account.tax.template">
<field name="name">4% IVA soportado (bienes corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_sc" model="account.tax.template">
<field name="name">10% IVA soportado (servicios corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0" model="account.tax.template">
<field name="name">IVA Exento Repercutido Sujeto</field>
<field name="l10n_es_type">exento</field>
<field name="l10n_es_exempt_reason">E1</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0_ns" model="account.tax.template">
<field name="name">IVA Exento Repercutido No Sujeto</field>
<field name="l10n_es_type">ignore</field>
</record>
<record id="l10n_es.account_tax_template_s_req0" model="account.tax.template">
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_p_req0" model="account.tax.template">
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_s_req05" model="account.tax.template">
<field name="name">0.50% Recargo Equivalencia Ventas</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_s_req062" model="account.tax.template">
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_p_req062" model="account.tax.template">
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_s_iva4b" model="account.tax.template">
<field name="name">IVA 4% (Bienes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva10b" model="account.tax.template">
<field name="name">IVA 10% (Bienes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva0_nd" model="account.tax.template">
<field name="name">21% IVA Soportado no deducible</field>
<field name="l10n_es_type">no_deducible</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_nd" model="account.tax.template">
<field name="name">10% IVA Soportado no deducible</field>
<field name="l10n_es_type">no_deducible</field>
</record>
<record id="l10n_es.account_tax_template_p_iva5_nd" model="account.tax.template">
<field name="name">5% IVA Soportado no deducible</field>
<field name="l10n_es_type">no_deducible</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_nd" model="account.tax.template">
<field name="name">4% IVA Soportado no deducible</field>
<field name="l10n_es_type">no_deducible</field>
</record>
<record id="l10n_es.account_tax_template_s_iva4s" model="account.tax.template">
<field name="name">IVA 4% (Servicios)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_s_iva10s" model="account.tax.template">
<field name="name">IVA 10% (Servicios)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_s_req014" model="account.tax.template">
<field name="name">1.4% Recargo Equivalencia Ventas</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_s_req52" model="account.tax.template">
<field name="name">5.2% Recargo Equivalencia Ventas</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_p_iva0_bc" model="account.tax.template">
<field name="name">IVA Soportado exento (operaciones corrientes)</field>
<field name="l10n_es_type">sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_iva0_ns" model="account.tax.template">
<field name="name">IVA Soportado no sujeto (Servicios)</field>
<field name="l10n_es_type">no_sujeto</field>
<field name="tax_scope">service</field>
</record>
<record id="l10n_es.account_tax_template_p_iva0_ns_b" model="account.tax.template">
<field name="name">IVA Soportado no sujeto (Bienes)</field>
<field name="l10n_es_type">no_sujeto</field>
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf9" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 9%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf18" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 18%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf19" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 19%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf19a" model="account.tax.template">
<field name="name">Retenciones a cuenta 19% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf195a" model="account.tax.template">
<field name="name">Retenciones a cuenta 19,5% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf19" model="account.tax.template">
<field name="name">Retenciones IRPF 19%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf20a" model="account.tax.template">
<field name="name">Retenciones 20% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf18" model="account.tax.template">
<field name="name">Retenciones IRPF 18%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf19a" model="account.tax.template">
<field name="name">Retenciones 19% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf195a" model="account.tax.template">
<field name="name">Retenciones 19,5% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf7" model="account.tax.template">
<field name="name">Retenciones IRPF 7%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf9" model="account.tax.template">
<field name="name">Retenciones IRPF 9%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf24" model="account.tax.template">
<field name="name">Retenciones IRPF 24%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf20" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 20%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf20a" model="account.tax.template">
<field name="name">Retenciones a cuenta 20% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf24" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 24%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_iva12_agr" model="account.tax.template">
<field name="name">12% IVA Soportado régimen agricultura</field>
<field name="l10n_es_type">sujeto_agricultura</field>
</record>
<record id="l10n_es.account_tax_template_p_iva105_gan" model="account.tax.template">
<field name="name">10,5% IVA Soportado régimen ganadero o pesca</field>
<field name="l10n_es_type">sujeto_agricultura</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0_e" model="account.tax.template">
<field name="name">IVA 0% Exportaciones</field>
<field name="l10n_es_type">exento</field>
<field name="l10n_es_exempt_reason">E2</field> <!--E2 for exportation-->
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0_ic" model="account.tax.template">
<field name="name">IVA 0% Entregas Intracomunitarias exentas</field>
<field name="l10n_es_type">exento</field>
<field name="l10n_es_exempt_reason">E5</field> <!--E5 for intra-community-->
<field name="tax_scope">consu</field>
</record>
<record id="l10n_es.account_tax_template_p_req014" model="account.tax.template">
<field name="name">1.4% Recargo Equivalencia Compras</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_p_req05" model="account.tax.template">
<field name="name">0.50% Recargo Equivalencia Compras</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_p_req52" model="account.tax.template">
<field name="name">5.2% Recargo Equivalencia Compras</field>
<field name="l10n_es_type">recargo</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf1" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 1%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf2" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 2%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf21" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 21%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf21a" model="account.tax.template">
<field name="name">Retenciones a cuenta 21% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf7" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 7%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpf15" model="account.tax.template">
<field name="name">Retenciones a cuenta IRPF 15%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf1" model="account.tax.template">
<field name="name">Retenciones IRPF 1%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf15" model="account.tax.template">
<field name="name">Retenciones IRPF 15%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf21t" model="account.tax.template">
<field name="name">Retenciones IRPF (Trabajadores)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_sp_in" model="account.tax.template">
<field name="name">IVA 10% Adquisición de servicios intracomunitarios</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_sp_in" model="account.tax.template">
<field name="name">IVA 4% Adquisición de servicios intracomunitarios</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf21te" model="account.tax.template">
<field name="name">Retenciones IRPF (Trabajadores) en especie</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf20" model="account.tax.template">
<field name="name">Retenciones IRPF 20%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf21a" model="account.tax.template">
<field name="name">Retenciones 21% (Arrendamientos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf21p" model="account.tax.template">
<field name="name">Retenciones IRPF 21%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpf2" model="account.tax.template">
<field name="name">Retenciones IRPF 2%</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_iva0_isp" model="account.tax.template">
<field name="name">IVA 0% Venta con Inversión del Sujeto Pasivo</field>
<field name="l10n_es_type">sujeto_isp</field>
</record>
<record id="l10n_es.account_tax_template_p_iva4_isp" model="account.tax.template">
<field name="name">IVA 4% Compra con Inversión del Sujeto Pasivo Nacional</field>
<field name="l10n_es_type">sujeto_isp</field>
</record>
<record id="l10n_es.account_tax_template_p_iva10_isp" model="account.tax.template">
<field name="name">IVA 10% Compra con Inversión del Sujeto Pasivo Nacional</field>
<field name="l10n_es_type">sujeto_isp</field>
</record>
<record id="l10n_es.account_tax_template_p_iva21_isp" model="account.tax.template">
<field name="name">IVA 21% Compra con Inversión del Sujeto Pasivo Nacional</field>
<field name="l10n_es_type">sujeto_isp</field>
</record>
<record id="l10n_es.account_tax_template_p_rp19" model="account.tax.template">
<field name="name">Retenciones 19% (préstamos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_rrD19" model="account.tax.template">
<field name="name">Retenciones 19% (reparto de dividendos)</field>
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpfnrnue24" model="account.tax.template">
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpfnrnue24p" model="account.tax.template">
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_s_irpfnrue19" model="account.tax.template">
<field name="l10n_es_type">retencion</field>
</record>
<record id="l10n_es.account_tax_template_p_irpfnrue19p" model="account.tax.template">
<field name="l10n_es_type">retencion</field>
</record>
</odoo>

View file

@ -0,0 +1,3 @@
-- disable_l10n_es_edi_integration
UPDATE res_company
SET l10n_es_edi_test_env = true;

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="partner_simplified" model="res.partner">
<field name="name">Simplified Invoice Partner (ES)</field>
</record>
</odoo>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="demo_certificate" model="l10n_es_edi.certificate">
<field name="content" type="base64" file="l10n_es_edi_sii/demo/certificates/aeat_1234.p12"/>
<field name="password">1234</field>
<field name="company_id" ref="l10n_es.demo_company_es"/>
</record>
</odoo>

View file

@ -0,0 +1,560 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_es_edi_sii
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-13 13:24+0000\n"
"PO-Revision-Date: 2025-02-13 13:24+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\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_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">Registro de Libros connection SII</span>\n"
" <span class=\"fa fa-lg fa-building-o\" title=\"Values set here are company-specific.\" groups=\"base.group_multi_company\"/>"
msgstr ""
"<span class=\"o_form_label\">Connexión con el Registro de Libros mediante SII</span>\n"
" <span class=\"fa fa-lg fa-building-o\" title=\"Los valores establecidos aquí son específicos de la compañía.\" groups=\"base.group_multi_company\"/>"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__aeat
msgid "Agencia Tributaria española"
msgstr "Agencia Tributaria española"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e1
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e1
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e1
msgid "Art. 20"
msgstr "Art. 20"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e2
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e2
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e2
msgid "Art. 21"
msgstr "Art. 21"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e3
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e3
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e3
msgid "Art. 22"
msgstr "Art. 22"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e4
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e4
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e4
msgid "Art. 23 y 24"
msgstr "Art. 23 y 24"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e5
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e5
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e5
msgid "Art. 25"
msgstr "Art. 25"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_bien_inversion
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_bien_inversion
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_bien_inversion
msgid "Bien de Inversion"
msgstr "Bien de inversión"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_edi_csv
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_edi_csv
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_edi_csv
msgid "CSV return code"
msgstr "Código de devolución CSV"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_certificate_id
msgid "Certificate (ES)"
msgstr "Certificado (ES)"
#. module: l10n_es_edi_sii
#: model:ir.ui.menu,name:l10n_es_edi_sii.menu_l10n_es_edi_certificates
msgid "Certificates (ES)"
msgstr "Certificados (ES)"
#. module: l10n_es_edi_sii
#: model:ir.actions.act_window,name:l10n_es_edi_sii.l10n_es_edi_certificate_action
msgid "Certificates for EDI invoices on Spain"
msgstr "Certificados para las facturas EDI en España"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_res_company
msgid "Companies"
msgstr "Compañías"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__company_id
msgid "Company"
msgstr "Compañía"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_res_config_settings
msgid "Config Settings"
msgstr "Ajustes de configuración"
#. module: l10n_es_edi_sii
#: model_terms:ir.actions.act_window,help:l10n_es_edi_sii.l10n_es_edi_certificate_action
msgid "Create the first certificate"
msgstr "Crear el primer certificado"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__create_date
msgid "Created on"
msgstr "Creado el"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_end
msgid "Date End"
msgstr "Fecha de finalización"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_start
msgid "Date Start"
msgstr "Fecha de inicio"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__display_name
msgid "Display Name"
msgstr "Nombre mostrado"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_edi_format
msgid "EDI format"
msgstr "Formato EDI"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_mail_template
msgid "Email Templates"
msgstr "Plantillas de correo electrónico"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_exempt_reason
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_exempt_reason
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason
msgid "Exempt Reason (Spain)"
msgstr "Motivo de exención (España)"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__exento
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__exento
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__exento
msgid "Exento"
msgstr "Exento"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__content
msgid "File"
msgstr "Archivo"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__bizkaia
msgid "Hacienda Foral de Bizkaia"
msgstr "Hacienda Foral de Bizkaia"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__gipuzkoa
msgid "Hacienda Foral de Gipuzkoa"
msgstr "Hacienda Foral de Gipuzkoa"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__id
msgid "ID"
msgstr "ID"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__ignore
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__ignore
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__ignore
msgid "Ignore even the base amount"
msgstr "Ignorar incluso el importe base"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_edi_is_required
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_edi_is_required
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_edi_is_required
msgid "Is the Spanish EDI needed"
msgstr "¿Se necesita el EDI español?"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_move
msgid "Journal Entry"
msgstr "Asiento contable"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_certificate_ids
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_certificate_ids
msgid "L10N Es Edi Certificate"
msgstr "L10N Es Certificado EDI"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate____last_update
msgid "Last Modified on"
msgstr "Última modificación el"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one main tax."
msgstr "La línea %s solo debe tener un impuesto principal."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one no sujeto (localizations) tax."
msgstr "La línea %s solo debe tener un impuesto no sujeto (localizaciones)."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one no sujeto tax."
msgstr "La línea %s solo debe tener un impuesto no sujeto."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one recargo tax."
msgstr "La línea %s solo debe tener un impuesto de recargo."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one retention tax."
msgstr "La línea %s solo debe tener un impuesto de retención."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one sujeto tax."
msgstr "La línea %s solo debe tener un impuesto sujeto."
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Manage certificates (SII/TicketBAI)"
msgstr "Gestionar certificados (SII/TicketBAI)"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid ""
"Networking error:\n"
"%s"
msgstr ""
"Error de red:\n"
"%s"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_deducible
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_deducible
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_deducible
msgid "No Deducible"
msgstr "No deducible"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_sujeto
msgid "No Sujeto"
msgstr "No sujeto"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_sujeto_loc
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_sujeto_loc
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_sujeto_loc
msgid "No Sujeto por reglas de Localization"
msgstr "No sujeto por reglas de localización"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "No tax agency selected: SII not activated."
msgstr "No se ha seleccionado ninguna agencia tributaria: SII no activado."
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e6
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e6
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e6
msgid "Otros"
msgstr "Otros"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__content
msgid "PFX Certificate"
msgstr "Certificado PFX"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__password
msgid "Passphrase for the PFX certificate"
msgstr "Frase de contraseña para el certificado PFX"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__password
msgid "Password"
msgstr "Contraseña"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_l10n_es_edi_certificate
msgid "Personal Digital Certificate"
msgstr "Certificado personal de persona física"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Please configure the certificate for SII."
msgstr "Configure el certificado para el SII."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Please specify a tax agency on your company for SII."
msgstr "Especifique una agencia tributaria en su compañía para el SII"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Production mode: EDI data is sent to the official agency servers."
msgstr ""
"Modo de producción: los datos EDI se envían a los servidores de la agencia "
"oficial."
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__recargo
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__recargo
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__recargo
msgid "Recargo de Equivalencia"
msgstr "Recargo de equivalencia"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_registration_date
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_registration_date
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_registration_date
msgid "Registration Date"
msgstr "Fecha de registro"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_resequence_wizard
msgid "Remake the sequence of Journal Entries."
msgstr "Remake the sequence of Journal Entries."
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__retencion
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__retencion
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__retencion
msgid "Retencion"
msgstr "Retención"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_l10n_es_sii_account_tax_mixin
msgid "SII Fields"
msgstr "Campos SII"
#. module: l10n_es_edi_sii
#: model:ir.ui.menu,name:l10n_es_edi_sii.menu_l10n_es_edi_root
msgid "Spain"
msgstr "España"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Spain Localization"
msgstr "Localización de España"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto
msgid "Sujeto"
msgstr "Sujeto"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto_agricultura
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto_agricultura
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto_agricultura
msgid "Sujeto Agricultura"
msgstr "Sujeto Agricultura"
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto_isp
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto_isp
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto_isp
msgid "Sujeto ISP"
msgstr "Sujeto ISP"
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_tax
msgid "Tax"
msgstr "Impuesto"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_tax_agency
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_tax_agency
msgid "Tax Agency for SII"
msgstr "Agencia Tributaria para SII"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_type
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_type
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_type
msgid "Tax Type (Spain)"
msgstr "Tipo de impuesto (España)"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"Tax agency selected: invoices will be sent by SII for journals where it is "
"activated."
msgstr ""
"Agencia tributaria seleccionada: las facturas serán enviadas por el SII para"
" los diarios en los que esté activado."
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_tax_template
msgid "Templates for Taxes"
msgstr "Plantillas para impuestos"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_test_env
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_test_env
msgid "Test Mode"
msgstr "Modo de prueba"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"Test mode: EDI data is sent to separate test servers and is not considered "
"official."
msgstr ""
"Modo de prueba: los datos EDI se envían a servidores de prueba "
"independientes y no se consideran oficiales."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "The SSL certificate could not be validated."
msgstr "No se ha podido validar el certificado SSL"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/l10n_es_edi_certificate.py:0
#, python-format
msgid "The certificate is expired since %s"
msgstr "El certificado ha caducado desde %s"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_end
msgid "The date on which the certificate expires"
msgstr "La fecha en la que el certificado caduca."
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_start
msgid "The date on which the certificate starts to be valid"
msgstr "La fecha en la que el certificado comienza a ser válido"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "The web service is not responding"
msgstr "El servicio web no responde"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/l10n_es_edi_certificate.py:0
#, python-format
msgid ""
"There has been a problem with the certificate, some usual problems can be:\n"
"- The password given or the certificate are not valid.\n"
"- The certificate content is invalid."
msgstr ""
"Había un problema con el certificado, algunos problemas habituales pueden ser:\n"
"La contraseña proporcionada o el certificado no son válidos.\n"
"El contenido del certificado no es válido"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "This was accepted with errors: "
msgstr "Esto se ha aceptado con errores:"
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_res_company__l10n_es_edi_test_env
#: model:ir.model.fields,help:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_test_env
msgid "Use the test environment"
msgstr "Utilizar el entorno de prueba"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "VAT number is missing on company %s"
msgstr "Falta el NIF de la compañía %s"
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.l10n_es_edi_certificate_form
msgid "Validity"
msgstr "Validez"
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid ""
"We saw that this invoice was sent correctly before, but we did not treat the"
" response. Make sure it is not because of a wrong configuration."
msgstr ""
"Esta factura se ha enviada correctamente antes, pero no procesamos la "
"respuesta. Asegúrese de que no se deba a una configuración incorrecta."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "You should put a vendor reference on this vendor bill. "
msgstr ""
"Debe poner una referencia del proveedor en esta factura del proveedor."
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "[%s] %s"
msgstr "[%s] %s"

View file

@ -0,0 +1,543 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_es_edi_sii
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-20 09:26+0000\n"
"PO-Revision-Date: 2025-05-20 09:26+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_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">Registro de Libros connection SII</span>\n"
" <span class=\"fa fa-lg fa-building-o\" title=\"Values set here are company-specific.\" groups=\"base.group_multi_company\"/>"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__aeat
msgid "Agencia Tributaria española"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e1
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e1
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e1
msgid "Art. 20"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e2
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e2
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e2
msgid "Art. 21"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e3
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e3
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e3
msgid "Art. 22"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e4
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e4
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e4
msgid "Art. 23 y 24"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e5
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e5
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e5
msgid "Art. 25"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_bien_inversion
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_bien_inversion
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_bien_inversion
msgid "Bien de Inversion"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_edi_csv
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_edi_csv
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_edi_csv
msgid "CSV return code"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_certificate_id
msgid "Certificate (ES)"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.ui.menu,name:l10n_es_edi_sii.menu_l10n_es_edi_certificates
msgid "Certificates (ES)"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.actions.act_window,name:l10n_es_edi_sii.l10n_es_edi_certificate_action
msgid "Certificates for EDI invoices on Spain"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_res_company
msgid "Companies"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__company_id
msgid "Company"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.actions.act_window,help:l10n_es_edi_sii.l10n_es_edi_certificate_action
msgid "Create the first certificate"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__create_uid
msgid "Created by"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__create_date
msgid "Created on"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_end
msgid "Date End"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_start
msgid "Date Start"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__display_name
msgid "Display Name"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_edi_format
msgid "EDI format"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_mail_template
msgid "Email Templates"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_exempt_reason
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_exempt_reason
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason
msgid "Exempt Reason (Spain)"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__exento
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__exento
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__exento
msgid "Exento"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__content
msgid "File"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__bizkaia
msgid "Hacienda Foral de Bizkaia"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__res_company__l10n_es_edi_tax_agency__gipuzkoa
msgid "Hacienda Foral de Gipuzkoa"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__id
msgid "ID"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__ignore
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__ignore
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__ignore
msgid "Ignore even the base amount"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_edi_is_required
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_edi_is_required
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_edi_is_required
msgid "Is the Spanish EDI needed"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_certificate_ids
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_certificate_ids
msgid "L10N Es Edi Certificate"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate____last_update
msgid "Last Modified on"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__write_uid
msgid "Last Updated by"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__write_date
msgid "Last Updated on"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one main tax."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one no sujeto (localizations) tax."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one no sujeto tax."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one recargo tax."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one retention tax."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Line %s should only have one sujeto tax."
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Manage certificates (SII/TicketBAI)"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid ""
"Networking error:\n"
"%s"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_deducible
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_deducible
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_deducible
msgid "No Deducible"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_sujeto
msgid "No Sujeto"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__no_sujeto_loc
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__no_sujeto_loc
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__no_sujeto_loc
msgid "No Sujeto por reglas de Localization"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "No tax agency selected: SII not activated."
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_exempt_reason__e6
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_exempt_reason__e6
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_exempt_reason__e6
msgid "Otros"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__content
msgid "PFX Certificate"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__password
msgid "Passphrase for the PFX certificate"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_edi_certificate__password
msgid "Password"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_l10n_es_edi_certificate
msgid "Personal Digital Certificate"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Please configure the certificate for SII."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "Please specify a tax agency on your company for SII."
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Production mode: EDI data is sent to the official agency servers."
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__recargo
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__recargo
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__recargo
msgid "Recargo de Equivalencia"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_bank_statement_line__l10n_es_registration_date
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_move__l10n_es_registration_date
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_payment__l10n_es_registration_date
msgid "Registration Date"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_resequence_wizard
msgid "Remake the sequence of Journal Entries."
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__retencion
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__retencion
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__retencion
msgid "Retencion"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_l10n_es_sii_account_tax_mixin
msgid "SII Fields"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.ui.menu,name:l10n_es_edi_sii.menu_l10n_es_edi_root
msgid "Spain"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid "Spain Localization"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto
msgid "Sujeto"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto_agricultura
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto_agricultura
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto_agricultura
msgid "Sujeto Agricultura"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax__l10n_es_type__sujeto_isp
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__account_tax_template__l10n_es_type__sujeto_isp
#: model:ir.model.fields.selection,name:l10n_es_edi_sii.selection__l10n_es_sii_account_tax_mixin__l10n_es_type__sujeto_isp
msgid "Sujeto ISP"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_tax
msgid "Tax"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_tax_agency
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_tax_agency
msgid "Tax Agency for SII"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax__l10n_es_type
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_account_tax_template__l10n_es_type
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_l10n_es_sii_account_tax_mixin__l10n_es_type
msgid "Tax Type (Spain)"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"Tax agency selected: invoices will be sent by SII for journals where it is "
"activated."
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model,name:l10n_es_edi_sii.model_account_tax_template
msgid "Templates for Taxes"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_company__l10n_es_edi_test_env
#: model:ir.model.fields,field_description:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_test_env
msgid "Test Mode"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.res_config_settings_view_form
msgid ""
"Test mode: EDI data is sent to separate test servers and is not considered "
"official."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "The SSL certificate could not be validated."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/l10n_es_edi_certificate.py:0
#, python-format
msgid "The certificate is expired since %s"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_end
msgid "The date on which the certificate expires"
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_l10n_es_edi_certificate__date_start
msgid "The date on which the certificate starts to be valid"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "The web service is not responding"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/l10n_es_edi_certificate.py:0
#, python-format
msgid ""
"There has been a problem with the certificate, some usual problems can be:\n"
"- The password given or the certificate are not valid.\n"
"- The certificate content is invalid."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "This was accepted with errors: "
msgstr ""
#. module: l10n_es_edi_sii
#: model:ir.model.fields,help:l10n_es_edi_sii.field_res_company__l10n_es_edi_test_env
#: model:ir.model.fields,help:l10n_es_edi_sii.field_res_config_settings__l10n_es_edi_test_env
msgid "Use the test environment"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "VAT number is missing on company %s"
msgstr ""
#. module: l10n_es_edi_sii
#: model_terms:ir.ui.view,arch_db:l10n_es_edi_sii.l10n_es_edi_certificate_form
msgid "Validity"
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid ""
"We saw that this invoice was sent correctly before, but we did not treat the"
" response. Make sure it is not because of a wrong configuration."
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "You should put a vendor reference on this vendor bill. "
msgstr ""
#. module: l10n_es_edi_sii
#. odoo-python
#: code:addons/l10n_es_edi_sii/models/account_edi_format.py:0
#, python-format
msgid "[%s] %s"
msgstr ""

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_edi_format
from . import account_move
from . import account_tax
from . import l10n_es_edi_certificate
from . import mail_template
from . import res_company
from . import res_config_settings

View file

@ -0,0 +1,738 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from urllib3.util.ssl_ import create_urllib3_context, DEFAULT_CIPHERS
from urllib3.contrib.pyopenssl import inject_into_urllib3
from OpenSSL.crypto import load_certificate, load_privatekey, FILETYPE_PEM
from odoo import fields, models, _
from odoo.tools import html_escape, zeep
import math
import json
import requests
# Custom patches to perform the WSDL requests.
EUSKADI_CIPHERS = f"{DEFAULT_CIPHERS}:!DH"
class PatchedHTTPAdapter(requests.adapters.HTTPAdapter):
""" An adapter to block DH ciphers which may not work for the tax agencies called"""
def init_poolmanager(self, *args, **kwargs):
# OVERRIDE
inject_into_urllib3()
kwargs['ssl_context'] = create_urllib3_context(ciphers=EUSKADI_CIPHERS)
return super().init_poolmanager(*args, **kwargs)
def cert_verify(self, conn, url, verify, cert):
# OVERRIDE
# The last parameter is only used by the super method to check if the file exists.
# In our case, cert is an odoo record 'l10n_es_edi.certificate' so not a path to a file.
# By putting 'None' as last parameter, we ensure the check about TLS configuration is
# still made without checking temporary files exist.
super().cert_verify(conn, url, verify, None)
conn.cert_file = cert
conn.key_file = None
def get_connection(self, url, proxies=None):
# OVERRIDE
# Patch the OpenSSLContext to decode the certificate in-memory.
conn = super().get_connection(url, proxies=proxies)
context = conn.conn_kw['ssl_context']
def patched_load_cert_chain(l10n_es_odoo_certificate, keyfile=None, password=None):
cert_file, key_file, _certificate = l10n_es_odoo_certificate.sudo()._decode_certificate()
cert_obj = load_certificate(FILETYPE_PEM, cert_file)
pkey_obj = load_privatekey(FILETYPE_PEM, key_file)
context._ctx.use_certificate(cert_obj)
context._ctx.use_privatekey(pkey_obj)
context.load_cert_chain = patched_load_cert_chain
return conn
class AccountEdiFormat(models.Model):
_inherit = 'account.edi.format'
# -------------------------------------------------------------------------
# ES EDI
# -------------------------------------------------------------------------
def _l10n_es_edi_get_invoices_tax_details_info(self, invoice, filter_invl_to_apply=None):
def grouping_key_generator(base_line, tax_values):
tax = tax_values['tax_repartition_line'].tax_id
return {
'applied_tax_amount': tax.amount,
'l10n_es_type': tax.l10n_es_type,
'l10n_es_exempt_reason': tax.l10n_es_exempt_reason if tax.l10n_es_type == 'exento' else False,
'l10n_es_bien_inversion': tax.l10n_es_bien_inversion,
}
def filter_to_apply(base_line, tax_values):
# For intra-community, we do not take into account the negative repartition line
return tax_values['tax_repartition_line'].factor_percent > 0.0
def full_filter_invl_to_apply(invoice_line):
if 'ignore' in invoice_line.tax_ids.flatten_taxes_hierarchy().mapped('l10n_es_type'):
return False
return filter_invl_to_apply(invoice_line) if filter_invl_to_apply else True
tax_details = invoice._prepare_edi_tax_details(
grouping_key_generator=grouping_key_generator,
filter_invl_to_apply=full_filter_invl_to_apply,
filter_to_apply=filter_to_apply,
)
sign = -1 if invoice.move_type in ('out_refund', 'in_refund') else 1
tax_details_info = defaultdict(dict)
# Detect for which is the main tax for 'recargo'. Since only a single combination tax + recargo is allowed
# on the same invoice, this can be deduced globally.
recargo_tax_details = {} # Mapping between main tax and recargo tax details
invoice_lines = invoice.invoice_line_ids.filtered(lambda x: x.display_type not in ('line_note', 'line_section'))
if filter_invl_to_apply:
invoice_lines = invoice_lines.filtered(filter_invl_to_apply)
for line in invoice_lines:
taxes = line.tax_ids.flatten_taxes_hierarchy()
recargo_tax = [t for t in taxes if t.l10n_es_type == 'recargo']
if recargo_tax and taxes:
recargo_main_tax = taxes.filtered(lambda x: x.l10n_es_type in ('sujeto', 'sujeto_isp'))[:1]
if not recargo_tax_details.get(recargo_main_tax):
recargo_tax_details[recargo_main_tax] = [
x for x in tax_details['tax_details'].values()
if x['group_tax_details'][0]['tax_repartition_line'].tax_id == recargo_tax[0]
][0]
tax_amount_deductible = 0.0
tax_amount_retention = 0.0
base_amount_not_subject = 0.0
base_amount_not_subject_loc = 0.0
has_not_subject = False
has_not_subject_loc = False
tax_subject_info_list = []
tax_subject_isp_info_list = []
for tax_values in tax_details['tax_details'].values():
if invoice.is_sale_document():
# Customer invoices
if tax_values['l10n_es_type'] in ('sujeto', 'sujeto_isp'):
tax_amount_deductible += tax_values['tax_amount']
base_amount = sign * tax_values['base_amount']
tax_info = {
'TipoImpositivo': tax_values['applied_tax_amount'],
'BaseImponible': round(base_amount, 2),
'CuotaRepercutida': round(math.copysign(tax_values['tax_amount'], base_amount), 2),
}
recargo = recargo_tax_details.get(tax_values['group_tax_details'][0]['tax_repartition_line'].tax_id)
if recargo:
tax_info['CuotaRecargoEquivalencia'] = round(sign * recargo['tax_amount'], 2)
tax_info['TipoRecargoEquivalencia'] = recargo['applied_tax_amount']
if tax_values['l10n_es_type'] == 'sujeto':
tax_subject_info_list.append(tax_info)
else:
tax_subject_isp_info_list.append(tax_info)
elif tax_values['l10n_es_type'] == 'exento':
tax_details_info['Sujeta'].setdefault('Exenta', {'DetalleExenta': []})
tax_details_info['Sujeta']['Exenta']['DetalleExenta'].append({
'BaseImponible': round(sign * tax_values['base_amount'], 2),
'CausaExencion': tax_values['l10n_es_exempt_reason'],
})
elif tax_values['l10n_es_type'] == 'retencion':
tax_amount_retention += tax_values['tax_amount']
elif tax_values['l10n_es_type'] == 'no_sujeto':
has_not_subject = True
base_amount_not_subject += tax_values['base_amount']
elif tax_values['l10n_es_type'] == 'no_sujeto_loc':
has_not_subject_loc = True
base_amount_not_subject_loc += tax_values['base_amount']
elif tax_values['l10n_es_type'] == 'ignore':
continue
else:
# Vendor bills
if tax_values['l10n_es_type'] in ('sujeto', 'sujeto_isp', 'no_sujeto', 'no_sujeto_loc'):
tax_amount_deductible += tax_values['tax_amount']
elif tax_values['l10n_es_type'] == 'retencion':
tax_amount_retention += tax_values['tax_amount']
elif tax_values['l10n_es_type'] == 'no_sujeto':
has_not_subject = True
base_amount_not_subject += tax_values['base_amount']
elif tax_values['l10n_es_type'] == 'no_sujeto_loc':
has_not_subject_loc = True
base_amount_not_subject_loc += tax_values['base_amount']
elif tax_values['l10n_es_type'] == 'ignore':
continue
if tax_values['l10n_es_type'] not in ['retencion', 'recargo']: # = in sujeto/sujeto_isp/no_deducible
base_amount = sign * tax_values['base_amount']
tax_details_info.setdefault('DetalleIVA', [])
tax_info = {
'BaseImponible': round(base_amount, 2),
}
if tax_values['l10n_es_type'] == 'sujeto_agricultura':
tax_info.update({
'PorcentCompensacionREAGYP': tax_values['applied_tax_amount'],
'ImporteCompensacionREAGYP': round(math.copysign(tax_values['tax_amount'], base_amount), 2),
})
elif tax_values['applied_tax_amount'] > 0.0:
tax_info.update({
'TipoImpositivo': tax_values['applied_tax_amount'],
'CuotaSoportada': round(math.copysign(tax_values['tax_amount'], base_amount), 2),
})
if tax_values['l10n_es_bien_inversion']:
tax_info['BienInversion'] = 'S'
recargo = recargo_tax_details.get(tax_values['group_tax_details'][0]['tax_repartition_line'].tax_id)
if recargo:
tax_info['CuotaRecargoEquivalencia'] = round(sign * recargo['tax_amount'], 2)
tax_info['TipoRecargoEquivalencia'] = recargo['applied_tax_amount']
tax_details_info['DetalleIVA'].append(tax_info)
if tax_subject_isp_info_list and not tax_subject_info_list: # Only for sale_invoices
tax_details_info['Sujeta']['NoExenta'] = {'TipoNoExenta': 'S2'}
elif not tax_subject_isp_info_list and tax_subject_info_list:
tax_details_info['Sujeta']['NoExenta'] = {'TipoNoExenta': 'S1'}
elif tax_subject_isp_info_list and tax_subject_info_list:
tax_details_info['Sujeta']['NoExenta'] = {'TipoNoExenta': 'S3'}
if tax_subject_info_list:
tax_details_info['Sujeta']['NoExenta'].setdefault('DesgloseIVA', {})
tax_details_info['Sujeta']['NoExenta']['DesgloseIVA'].setdefault('DetalleIVA', [])
tax_details_info['Sujeta']['NoExenta']['DesgloseIVA']['DetalleIVA'] += tax_subject_info_list
if tax_subject_isp_info_list:
tax_details_info['Sujeta']['NoExenta'].setdefault('DesgloseIVA', {})
tax_details_info['Sujeta']['NoExenta']['DesgloseIVA'].setdefault('DetalleIVA', [])
tax_details_info['Sujeta']['NoExenta']['DesgloseIVA']['DetalleIVA'] += tax_subject_isp_info_list
if has_not_subject and invoice.is_sale_document():
tax_details_info['NoSujeta']['ImportePorArticulos7_14_Otros'] = round(sign * base_amount_not_subject, 2)
if has_not_subject_loc and invoice.is_sale_document():
tax_details_info['NoSujeta']['ImporteTAIReglasLocalizacion'] = round(sign * base_amount_not_subject_loc, 2)
return {
'tax_details_info': tax_details_info,
'tax_details': tax_details,
'tax_amount_deductible': tax_amount_deductible,
'tax_amount_retention': tax_amount_retention,
'base_amount_not_subject': base_amount_not_subject,
'S1_list': tax_subject_info_list, #TBAI has separate sections for S1 and S2
'S2_list': tax_subject_isp_info_list, #TBAI has separate sections for S1 and S2
}
def _l10n_es_edi_get_partner_info(self, partner):
eu_country_codes = set(self.env.ref('base.europe').country_ids.mapped('code'))
partner_info = {}
IDOtro_ID = partner.vat or 'NO_DISPONIBLE'
if (not partner.country_id or partner.country_id.code == 'ES') and partner.vat:
# ES partner with VAT.
partner_info['NIF'] = partner.vat[2:] if partner.vat.startswith('ES') else partner.vat
if self.env.context.get('error_1117'):
partner_info['IDOtro'] = {'IDType': '07', 'ID': IDOtro_ID}
elif partner.country_id.code in eu_country_codes and partner.vat:
# European partner.
partner_info['IDOtro'] = {'IDType': '02', 'ID': IDOtro_ID}
else:
partner_info['IDOtro'] = {'ID': IDOtro_ID}
if partner.vat:
partner_info['IDOtro']['IDType'] = '04'
else:
partner_info['IDOtro']['IDType'] = '06'
if partner.country_id:
partner_info['IDOtro']['CodigoPais'] = partner.country_id.code
return partner_info
def _l10n_es_edi_get_invoices_info(self, invoices):
eu_country_codes = set(self.env.ref('base.europe').country_ids.mapped('code'))
simplified_partner = self.env.ref("l10n_es_edi_sii.partner_simplified")
info_list = []
for invoice in invoices:
com_partner = invoice.commercial_partner_id
is_simplified = invoice.partner_id == simplified_partner
info = {
'PeriodoLiquidacion': {
'Ejercicio': str(invoice.date.year),
'Periodo': str(invoice.date.month).zfill(2),
},
'IDFactura': {
'FechaExpedicionFacturaEmisor': invoice.invoice_date.strftime('%d-%m-%Y'),
},
}
if invoice.is_sale_document():
invoice_node = info['FacturaExpedida'] = {}
else:
invoice_node = info['FacturaRecibida'] = {}
# === Partner ===
partner_info = self._l10n_es_edi_get_partner_info(com_partner)
# === Invoice ===
invoice_node['DescripcionOperacion'] = invoice.invoice_origin[:500] if invoice.invoice_origin else 'manual'
reagyp = invoice.invoice_line_ids.tax_ids.filtered(lambda t: t.l10n_es_type == 'sujeto_agricultura')
if invoice.is_sale_document():
nif = invoice.company_id.vat[2:] if invoice.company_id.vat.startswith('ES') else invoice.company_id.vat
info['IDFactura']['IDEmisorFactura'] = {'NIF': nif}
info['IDFactura']['NumSerieFacturaEmisor'] = invoice.name[:60]
if not is_simplified:
invoice_node['Contraparte'] = {
**partner_info,
'NombreRazon': com_partner.name[:120],
}
export_exempts = invoice.invoice_line_ids.tax_ids.filtered(lambda t: t.l10n_es_exempt_reason == 'E2')
# If an invoice line contains an OSS tax, the invoice is considered as an OSS operation
is_oss = self._has_oss_taxes(invoice)
if is_oss:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '17'
elif export_exempts:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '02'
else:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '01'
else:
info['IDFactura']['IDEmisorFactura'] = partner_info
# In case of cancel
info["IDFactura"]["IDEmisorFactura"].update(
{"NombreRazon": com_partner.name[0:120]}
)
info["IDFactura"]["NumSerieFacturaEmisor"] = (invoice.ref or "")[:60]
if not is_simplified:
invoice_node['Contraparte'] = {
**partner_info,
'NombreRazon': com_partner.name[:120],
}
if invoice.l10n_es_registration_date:
invoice_node['FechaRegContable'] = invoice.l10n_es_registration_date.strftime('%d-%m-%Y')
else:
invoice_node['FechaRegContable'] = fields.Date.context_today(self).strftime('%d-%m-%Y')
mod_303_10 = self.env.ref('l10n_es.mod_303_10')
mod_303_11 = self.env.ref('l10n_es.mod_303_11')
tax_tags = invoice.invoice_line_ids.tax_ids.invoice_repartition_line_ids.tag_ids
intracom = bool(tax_tags & (mod_303_10 + mod_303_11))
if intracom:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '09'
elif reagyp:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '02'
else:
invoice_node['ClaveRegimenEspecialOTrascendencia'] = '01'
if invoice.move_type == 'out_invoice':
invoice_node['TipoFactura'] = 'F2' if is_simplified else 'F1'
elif invoice.move_type == 'out_refund':
invoice_node['TipoFactura'] = 'R5' if is_simplified else 'R1'
invoice_node['TipoRectificativa'] = 'I'
elif invoice.move_type == 'in_invoice':
if reagyp:
invoice_node['TipoFactura'] = 'F6'
else:
invoice_node['TipoFactura'] = 'F1'
elif invoice.move_type == 'in_refund':
invoice_node['TipoFactura'] = 'R4'
invoice_node['TipoRectificativa'] = 'I'
# === Taxes ===
sign = -1 if invoice.move_type in ('out_refund', 'in_refund') else 1
if invoice.is_sale_document():
# Customer invoices
if (com_partner.country_id.code in ('ES', False) and not (com_partner.vat or '').startswith("ESN")) or is_simplified:
tax_details_info_vals = self._l10n_es_edi_get_invoices_tax_details_info(invoice)
invoice_node['TipoDesglose'] = {'DesgloseFactura': tax_details_info_vals['tax_details_info']}
invoice_node['ImporteTotal'] = round(sign * (
tax_details_info_vals['tax_details']['base_amount']
+ tax_details_info_vals['tax_details']['tax_amount']
- tax_details_info_vals['tax_amount_retention']
), 2)
else:
tax_details_info_service_vals = self._l10n_es_edi_get_invoices_tax_details_info(
invoice,
filter_invl_to_apply=lambda x: any(t.tax_scope == 'service' for t in x.tax_ids)
)
tax_details_info_consu_vals = self._l10n_es_edi_get_invoices_tax_details_info(
invoice,
filter_invl_to_apply=lambda x: any(t.tax_scope == 'consu' for t in x.tax_ids)
)
if tax_details_info_service_vals['tax_details_info']:
invoice_node.setdefault('TipoDesglose', {})
invoice_node['TipoDesglose'].setdefault('DesgloseTipoOperacion', {})
invoice_node['TipoDesglose']['DesgloseTipoOperacion']['PrestacionServicios'] = tax_details_info_service_vals['tax_details_info']
if tax_details_info_consu_vals['tax_details_info']:
invoice_node.setdefault('TipoDesglose', {})
invoice_node['TipoDesglose'].setdefault('DesgloseTipoOperacion', {})
invoice_node['TipoDesglose']['DesgloseTipoOperacion']['Entrega'] = tax_details_info_consu_vals['tax_details_info']
invoice_node['ImporteTotal'] = round(sign * (
tax_details_info_service_vals['tax_details']['base_amount']
+ tax_details_info_service_vals['tax_details']['tax_amount']
- tax_details_info_service_vals['tax_amount_retention']
+ tax_details_info_consu_vals['tax_details']['base_amount']
+ tax_details_info_consu_vals['tax_details']['tax_amount']
- tax_details_info_consu_vals['tax_amount_retention']
), 2)
else:
# Vendor bills
tax_details_info_isp_vals = self._l10n_es_edi_get_invoices_tax_details_info(
invoice,
filter_invl_to_apply=lambda x: any(t for t in x.tax_ids if t.l10n_es_type == 'sujeto_isp'),
)
tax_details_info_other_vals = self._l10n_es_edi_get_invoices_tax_details_info(
invoice,
filter_invl_to_apply=lambda x: not any(t for t in x.tax_ids if t.l10n_es_type == 'sujeto_isp'),
)
invoice_node['DesgloseFactura'] = {}
if tax_details_info_isp_vals['tax_details_info']:
invoice_node['DesgloseFactura']['InversionSujetoPasivo'] = tax_details_info_isp_vals['tax_details_info']
if tax_details_info_other_vals['tax_details_info']:
invoice_node['DesgloseFactura']['DesgloseIVA'] = tax_details_info_other_vals['tax_details_info']
if any(t.l10n_es_type == 'ignore' for t in invoice.invoice_line_ids.tax_ids):
invoice_node['ImporteTotal'] = round(sign * (
tax_details_info_isp_vals['tax_details']['base_amount']
+ tax_details_info_isp_vals['tax_details']['tax_amount']
+ tax_details_info_other_vals['tax_details']['base_amount']
+ tax_details_info_other_vals['tax_details']['tax_amount']
), 2)
else: # Intra-community -100 repartition line needs to be taken into account
invoice_node['ImporteTotal'] = round(-invoice.amount_total_signed
- sign * tax_details_info_isp_vals['tax_amount_retention']
- sign * tax_details_info_other_vals['tax_amount_retention'], 2)
invoice_node['CuotaDeducible'] = round(sign * (
tax_details_info_isp_vals['tax_amount_deductible']
+ tax_details_info_other_vals['tax_amount_deductible']
), 2)
info_list.append(info)
return info_list
def _l10n_es_edi_web_service_aeat_vals(self, invoices):
if invoices[0].is_sale_document():
return {
'url': 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/ssii_1_1/fact/ws/SuministroFactEmitidas.wsdl',
'test_url': 'https://prewww1.aeat.es/wlpl/SSII-FACT/ws/fe/SiiFactFEV1SOAP',
}
else:
return {
'url': 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/ssii_1_1/fact/ws/SuministroFactRecibidas.wsdl',
'test_url': 'https://prewww1.aeat.es/wlpl/SSII-FACT/ws/fr/SiiFactFRV1SOAP',
}
def _l10n_es_edi_web_service_bizkaia_vals(self, invoices):
if invoices[0].is_sale_document():
return {
'url': 'https://www.bizkaia.eus/ogasuna/sii/documentos/SuministroFactEmitidas.wsdl',
'test_url': 'https://pruapps.bizkaia.eus/SSII-FACT/ws/fe/SiiFactFEV1SOAP',
}
else:
return {
'url': 'https://www.bizkaia.eus/ogasuna/sii/documentos/SuministroFactRecibidas.wsdl',
'test_url': 'https://pruapps.bizkaia.eus/SSII-FACT/ws/fr/SiiFactFRV1SOAP',
}
def _l10n_es_edi_web_service_gipuzkoa_vals(self, invoices):
if invoices[0].is_sale_document():
return {
'url': 'https://egoitza.gipuzkoa.eus/ogasuna/sii/ficheros/v1.1/SuministroFactEmitidas.wsdl',
'test_url': 'https://sii-prep.egoitza.gipuzkoa.eus/JBS/HACI/SSII-FACT/ws/fe/SiiFactFEV1SOAP',
}
else:
return {
'url': 'https://egoitza.gipuzkoa.eus/ogasuna/sii/ficheros/v1.1/SuministroFactRecibidas.wsdl',
'test_url': 'https://sii-prep.egoitza.gipuzkoa.eus/JBS/HACI/SSII-FACT/ws/fr/SiiFactFRV1SOAP',
}
def _l10n_es_edi_call_web_service_sign(self, invoices, info_list):
return self._l10n_es_edi_call_web_service_sign_common(invoices, info_list)
def _l10n_es_edi_call_web_service_sign_common(self, invoices, info_list, cancel=False):
company = invoices.company_id
# All are sharing the same value.
csv_number = invoices.mapped('l10n_es_edi_csv')[0]
# Set registration date
invoices.filtered(lambda inv: not inv.l10n_es_registration_date).write({
'l10n_es_registration_date': fields.Date.context_today(self),
})
# === Call the web service ===
# Get connection data.
l10n_es_edi_tax_agency = company.mapped('l10n_es_edi_tax_agency')[0]
connection_vals = getattr(self, f'_l10n_es_edi_web_service_{l10n_es_edi_tax_agency}_vals')(invoices)
header = {
'IDVersionSii': '1.1',
'Titular': {
'NombreRazon': company.name[:120],
'NIF': company.vat[2:] if company.vat.startswith('ES') else company.vat,
},
'TipoComunicacion': 'A1' if csv_number else 'A0',
}
session = requests.Session()
session.cert = company.l10n_es_edi_certificate_id
session.mount('https://', PatchedHTTPAdapter())
client = zeep.Client(connection_vals['url'], operation_timeout=60, timeout=60, session=session)
if invoices[0].is_sale_document():
service_name = 'SuministroFactEmitidas'
else:
service_name = 'SuministroFactRecibidas'
if company.l10n_es_edi_test_env and not connection_vals.get('test_url'):
service_name += 'Pruebas'
# Establish the connection.
serv = client.bind('siiService', service_name)
if company.l10n_es_edi_test_env and connection_vals.get('test_url'):
serv._binding_options['address'] = connection_vals['test_url']
error_msg = None
try:
if cancel:
if invoices[0].is_sale_document():
res = serv.AnulacionLRFacturasEmitidas(header, info_list)
else:
res = serv.AnulacionLRFacturasRecibidas(header, info_list)
else:
if invoices[0].is_sale_document():
res = serv.SuministroLRFacturasEmitidas(header, info_list)
else:
res = serv.SuministroLRFacturasRecibidas(header, info_list)
except requests.exceptions.SSLError as error:
error_msg = _("The SSL certificate could not be validated.")
except (zeep.exceptions.Error, requests.exceptions.ConnectionError) as error:
error_msg = _("Networking error:\n%s", error)
except Exception as error:
error_msg = str(error)
if error_msg:
return {inv: {
'error': error_msg,
'blocking_level': 'warning',
} for inv in invoices}
# Process response.
if not res or not res.RespuestaLinea:
return {inv: {
'error': _("The web service is not responding"),
'blocking_level': 'warning',
} for inv in invoices}
resp_state = res["EstadoEnvio"]
l10n_es_edi_csv = res['CSV']
if resp_state == 'Correcto':
invoices.write({'l10n_es_edi_csv': l10n_es_edi_csv})
return {inv: {'success': True} for inv in invoices}
results = {}
for respl in res.RespuestaLinea:
invoice_number = respl.IDFactura.NumSerieFacturaEmisor
# Retrieve the corresponding invoice.
# Note: ref can be the same for different partners but there is no enough information on the response
# to match the partner.
# Note: Invoices are batched per move_type.
if invoices[0].is_sale_document():
inv = invoices.filtered(lambda x: x.name[:60] == invoice_number)
else:
# 'ref' can be the same for different partners.
candidates = invoices.filtered(lambda x: x.ref[:60] == invoice_number)
if len(candidates) >= 1:
respl_partner_info = respl.IDFactura.IDEmisorFactura
inv = None
for candidate in candidates:
partner_info = self._l10n_es_edi_get_partner_info(candidate.commercial_partner_id)
if partner_info.get('NIF') and partner_info['NIF'] == respl_partner_info.NIF:
inv = candidate
break
if (
partner_info.get('IDOtro')
and respl_partner_info['IDOtro']
and all(respl_partner_info['IDOtro'][k] == v for k, v in partner_info['IDOtro'].items())
):
inv = candidate
break
if not inv:
# This case shouldn't happen and means there is something wrong in this code. However, we can't
# raise anything since the document has already been approved by the government. The result
# will only be a badly logged message into the chatter so, not a big deal.
inv = candidates[0]
else:
inv = candidates
resp_line_state = respl.EstadoRegistro
respl_dict = dict(respl)
if resp_line_state in ('Correcto', 'AceptadoConErrores'):
inv.l10n_es_edi_csv = l10n_es_edi_csv
results[inv] = {'success': True}
if resp_line_state == 'AceptadoConErrores':
inv.message_post(body=_("This was accepted with errors: ") + html_escape(respl.DescripcionErrorRegistro))
elif (
(respl_dict.get('RegistroDuplicado') and respl.RegistroDuplicado.EstadoRegistro == 'Correcta')
or
(cancel and respl_dict.get('CodigoErrorRegistro') == 3001)
):
results[inv] = {'success': True}
inv.message_post(body=_("We saw that this invoice was sent correctly before, but we did not treat "
"the response. Make sure it is not because of a wrong configuration."))
elif respl.CodigoErrorRegistro == 1117 and not self.env.context.get('error_1117'):
return self.with_context(error_1117=True)._l10n_es_edi_sii_post_invoices(invoices)
else:
results[inv] = {
'error': _("[%s] %s", respl.CodigoErrorRegistro, respl.DescripcionErrorRegistro),
'blocking_level': 'error',
}
return results
def _has_oss_taxes(self, invoice):
oss_tag = self.env.ref('l10n_eu_oss.tag_oss', raise_if_not_found=False)
lines = invoice.invoice_line_ids.filtered(lambda line: line.display_type not in ('line_section', 'line_note'))
tags = (lines.tax_ids.invoice_repartition_line_ids | lines.tax_ids.refund_repartition_line_ids).tag_ids
return bool(oss_tag and oss_tag in tags)
# -------------------------------------------------------------------------
# EDI OVERRIDDEN METHODS
# -------------------------------------------------------------------------
def _l10n_es_edi_sii_xml_invoice_content(self, invoice):
return json.dumps(self._l10n_es_edi_get_invoices_info(invoice)).encode()
def _get_move_applicability(self, move):
# EXTENDS account_edi
self.ensure_one()
if self.code != 'es_sii':
return super()._get_move_applicability(move)
if move.l10n_es_edi_is_required:
return {
'post': self._l10n_es_edi_sii_post_invoices,
'post_batching': lambda invoice: (invoice.move_type, invoice.l10n_es_edi_csv),
'edi_content': self._l10n_es_edi_sii_xml_invoice_content,
'cancel': self._l10n_es_edi_sii_cancel_invoices,
}
def _needs_web_services(self):
# OVERRIDE
return self.code == 'es_sii' or super()._needs_web_services()
def _check_move_configuration(self, move):
# OVERRIDE
res = super()._check_move_configuration(move)
if self.code != 'es_sii':
return res
if not move.company_id.vat:
res.append(_("VAT number is missing on company %s", move.company_id.display_name))
for line in move.invoice_line_ids.filtered(lambda line: line.display_type not in ('line_note', 'line_section')):
taxes = line.tax_ids.flatten_taxes_hierarchy()
recargo_count = taxes.mapped('l10n_es_type').count('recargo')
retention_count = taxes.mapped('l10n_es_type').count('retencion')
sujeto_count = taxes.mapped('l10n_es_type').count('sujeto')
no_sujeto_count = taxes.mapped('l10n_es_type').count('no_sujeto')
no_sujeto_loc_count = taxes.mapped('l10n_es_type').count('no_sujeto_loc')
if retention_count > 1:
res.append(_("Line %s should only have one retention tax.", line.display_name))
if recargo_count > 1:
res.append(_("Line %s should only have one recargo tax.", line.display_name))
if sujeto_count > 1:
res.append(_("Line %s should only have one sujeto tax.", line.display_name))
if no_sujeto_count > 1:
res.append(_("Line %s should only have one no sujeto tax.", line.display_name))
if no_sujeto_loc_count > 1:
res.append(_("Line %s should only have one no sujeto (localizations) tax.", line.display_name))
if sujeto_count + no_sujeto_loc_count + no_sujeto_count > 1:
res.append(_("Line %s should only have one main tax.", line.display_name))
if move.move_type in ('in_invoice', 'in_refund'):
if not move.ref:
res.append(_("You should put a vendor reference on this vendor bill. "))
return res
def _is_compatible_with_journal(self, journal):
# OVERRIDE
if self.code != 'es_sii':
return super()._is_compatible_with_journal(journal)
return journal.country_code == 'ES'
def _l10n_es_edi_sii_send(self, invoices, cancel=False):
# Ensure a certificate is available.
certificate = invoices.company_id.l10n_es_edi_certificate_id
if not certificate:
return {inv: {
'error': _("Please configure the certificate for SII."),
'blocking_level': 'error',
} for inv in invoices}
# Ensure a tax agency is available.
l10n_es_edi_tax_agency = invoices.company_id.mapped('l10n_es_edi_tax_agency')[0]
if not l10n_es_edi_tax_agency:
return {inv: {
'error': _("Please specify a tax agency on your company for SII."),
'blocking_level': 'error',
} for inv in invoices}
# Generate the JSON.
info_list = self._l10n_es_edi_get_invoices_info(invoices)
# Call the web service.
if not cancel: #retrocompatibility and mocks in tests
res = self._l10n_es_edi_call_web_service_sign(invoices, info_list)
else:
res = self._l10n_es_edi_call_web_service_sign_common(invoices, info_list, cancel=True)
for inv in invoices:
if res.get(inv, {}).get('success'):
attachment = self.env['ir.attachment'].create({
'type': 'binary',
'name': 'jsondump.json',
'raw': json.dumps(info_list),
'mimetype': 'application/json',
'res_model': inv._name,
'res_id': inv.id,
})
res[inv]['attachment'] = attachment
if cancel:
inv.l10n_es_edi_csv = False
return res
def _l10n_es_edi_sii_post_invoices(self, invoices):
return self._l10n_es_edi_sii_send(invoices)
def _l10n_es_edi_sii_cancel_invoices(self, invoices):
return self._l10n_es_edi_sii_send(invoices, cancel=True)

View file

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class AccountMove(models.Model):
_inherit = 'account.move'
l10n_es_edi_is_required = fields.Boolean(
string="Is the Spanish EDI needed",
compute='_compute_l10n_es_edi_is_required'
)
l10n_es_edi_csv = fields.Char(string="CSV return code", copy=False, tracking=True)
# Technical field to keep the date the invoice was sent the first time as
# the date the invoice was registered into the system.
l10n_es_registration_date = fields.Date(
string="Registration Date", copy=False,
)
# -------------------------------------------------------------------------
# COMPUTE METHODS
# -------------------------------------------------------------------------
@api.depends('move_type', 'company_id')
def _compute_l10n_es_edi_is_required(self):
for move in self:
move.l10n_es_edi_is_required = move.is_invoice() \
and move.country_code == 'ES' \
and move.company_id.l10n_es_edi_tax_agency
def _check_edi_documents_for_reset_to_draft(self):
docs = self.edi_document_ids.filtered(lambda d: d.edi_format_id._needs_web_services())
if len(docs) == 1 and docs.edi_format_id.code == 'es_sii' and docs.state != 'to_cancel':
return True
return super()._check_edi_documents_for_reset_to_draft()
def _edi_allow_button_draft(self):
docs = self.edi_document_ids.filtered(lambda d: d.edi_format_id._needs_web_services())
if len(docs) == 1 and docs.edi_format_id.code == 'es_sii' and docs.state != 'to_cancel':
return True
return super()._edi_allow_button_draft()

View file

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class SIIAccountTaxMixin(models.AbstractModel):
_name = 'l10n_es.sii.account.tax.mixin'
_description = 'SII Fields'
l10n_es_exempt_reason = fields.Selection(
selection=[
('E1', 'Art. 20'),
('E2', 'Art. 21'),
('E3', 'Art. 22'),
('E4', 'Art. 23 y 24'),
('E5', 'Art. 25'),
('E6', 'Otros'),
],
string="Exempt Reason (Spain)",
)
l10n_es_type = fields.Selection(
selection=[
('exento', 'Exento'),
('sujeto', 'Sujeto'),
('sujeto_agricultura', 'Sujeto Agricultura'),
('sujeto_isp', 'Sujeto ISP'),
('no_sujeto', 'No Sujeto'),
('no_sujeto_loc', 'No Sujeto por reglas de Localization'),
('no_deducible', 'No Deducible'),
('retencion', 'Retencion'),
('recargo', 'Recargo de Equivalencia'),
('ignore', 'Ignore even the base amount'),
],
string="Tax Type (Spain)", default='sujeto'
)
l10n_es_bien_inversion = fields.Boolean('Bien de Inversion', default=False)
class AccountTax(models.Model):
_inherit = ['account.tax', 'l10n_es.sii.account.tax.mixin']
_name = 'account.tax'
class AccountTaxTemplate(models.Model):
_inherit = ['account.tax.template', 'l10n_es.sii.account.tax.mixin']
_name = 'account.tax.template'
def _get_tax_vals(self, company, tax_template_to_tax):
# OVERRIDE
# Copy values from 'account.tax.template' to vals will be used to create a new 'account.tax'.
vals = super()._get_tax_vals(company, tax_template_to_tax)
vals['l10n_es_exempt_reason'] = self.l10n_es_exempt_reason
vals['l10n_es_type'] = self.l10n_es_type
vals['l10n_es_bien_inversion'] = self.l10n_es_bien_inversion
return vals

View file

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from base64 import b64decode
from pytz import timezone
from datetime import datetime
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat
from odoo import _, api, fields, models, tools
from odoo.exceptions import ValidationError
from odoo.addons.account.tools.certificate import load_key_and_certificates
class Certificate(models.Model):
_name = 'l10n_es_edi.certificate'
_description = 'Personal Digital Certificate'
_order = 'date_start desc, id desc'
_rec_name = 'date_start'
content = fields.Binary(string="File", required=True, help="PFX Certificate")
password = fields.Char(help="Passphrase for the PFX certificate", groups="base.group_system")
date_start = fields.Datetime(readonly=True, help="The date on which the certificate starts to be valid")
date_end = fields.Datetime(readonly=True, help="The date on which the certificate expires")
company_id = fields.Many2one(comodel_name='res.company', required=True, default=lambda self: self.env.company)
# -------------------------------------------------------------------------
# HELPERS
# -------------------------------------------------------------------------
@api.model
def _get_es_current_datetime(self):
"""Get the current datetime with the Peruvian timezone. """
return datetime.now(timezone('Europe/Madrid'))
@tools.ormcache('self.content', 'self.password')
def _decode_certificate(self):
"""Return the content (DER encoded) and the certificate decrypted based in the point 3.1 from the RS 097-2012
http://www.vauxoo.com/r/manualdeautorizacion#page=21
"""
self.ensure_one()
if not self.password:
return None, None, None
private_key, certificate = load_key_and_certificates(
b64decode(self.content),
self.password.encode(),
)
pem_certificate = certificate.public_bytes(Encoding.PEM)
pem_private_key = private_key.private_bytes(
Encoding.PEM,
format=PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=NoEncryption(),
)
return pem_certificate, pem_private_key, certificate
# -------------------------------------------------------------------------
# LOW-LEVEL METHODS
# -------------------------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
certificates = super().create(vals_list)
spain_tz = timezone('Europe/Madrid')
spain_dt = self._get_es_current_datetime()
for certificate in certificates:
try:
_pem_certificate, _pem_private_key, certif = certificate._decode_certificate()
cert_date_start = spain_tz.localize(certif.not_valid_before)
cert_date_end = spain_tz.localize(certif.not_valid_after)
except Exception:
raise ValidationError(_(
"There has been a problem with the certificate, some usual problems can be:\n"
"- The password given or the certificate are not valid.\n"
"- The certificate content is invalid."
))
# Assign extracted values from the certificate
certificate.write({
'date_start': fields.Datetime.to_string(cert_date_start),
'date_end': fields.Datetime.to_string(cert_date_end),
})
if spain_dt > cert_date_end:
raise ValidationError(_("The certificate is expired since %s", certificate.date_end))
return certificates

View file

@ -0,0 +1,12 @@
from odoo import models
class MailTemplate(models.Model):
_inherit = "mail.template"
def _get_edi_attachments(self, document):
# When an invoice is signed, an electronic document is created (i.e. jsondump.json)
# Prevent this document to be added in "SEND & PRINT" wizard
if document.name == 'jsondump.json' and document.edi_format_id.code == 'es_sii':
return {}
return super()._get_edi_attachments(document)

View file

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class ResCompany(models.Model):
_inherit = 'res.company'
l10n_es_edi_certificate_id = fields.Many2one(
string="Certificate (ES)",
store=True,
readonly=False,
comodel_name='l10n_es_edi.certificate',
compute="_compute_l10n_es_edi_certificate",
)
l10n_es_edi_certificate_ids = fields.One2many(
comodel_name='l10n_es_edi.certificate',
inverse_name='company_id',
)
l10n_es_edi_tax_agency = fields.Selection(
string="Tax Agency for SII",
selection=[
('aeat', "Agencia Tributaria española"),
('gipuzkoa', "Hacienda Foral de Gipuzkoa"),
('bizkaia', "Hacienda Foral de Bizkaia"),
],
default=False,
)
l10n_es_edi_test_env = fields.Boolean(
string="Test Mode",
help="Use the test environment",
default=True,
)
@api.depends('country_id', 'l10n_es_edi_certificate_ids')
def _compute_l10n_es_edi_certificate(self):
for company in self:
if company.country_code == 'ES':
company.l10n_es_edi_certificate_id = self.env['l10n_es_edi.certificate'].search(
[('company_id', '=', company.id)],
order='date_end desc',
limit=1,
)
else:
company.l10n_es_edi_certificate_id = False

View file

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
l10n_es_edi_certificate_ids = fields.One2many(related='company_id.l10n_es_edi_certificate_ids', readonly=False)
l10n_es_edi_tax_agency = fields.Selection(related='company_id.l10n_es_edi_tax_agency', readonly=False)
l10n_es_edi_test_env = fields.Boolean(related='company_id.l10n_es_edi_test_env', readonly=False)

View file

@ -0,0 +1,2 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_l10n_es_edi_certificate,access_l10n_es_edi_certificate,model_l10n_es_edi_certificate,base.group_system,1,1,1,1
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_l10n_es_edi_certificate access_l10n_es_edi_certificate model_l10n_es_edi_certificate base.group_system 1 1 1 1

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Allow access to certificates of current company/companies -->
<record id="l10n_ec_digital_certificate" model="ir.rule">
<field name="name">Spanish Digital Certificate</field>
<field name="model_id" ref="l10n_es_edi_sii.model_l10n_es_edi_certificate"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
</odoo>

View file

@ -0,0 +1,5 @@
# coding: utf-8
from . import test_edi_xml
from . import test_edi_web_services
from . import test_resequence

View file

@ -0,0 +1,83 @@
# coding: utf-8
import base64
from pytz import timezone
from datetime import datetime
from odoo.tools import misc
from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
def mocked_l10n_es_edi_call_web_service_sign(edi_format, invoices, info_list):
return {inv: {"success": True} for inv in invoices}
class TestEsEdiCommon(AccountEdiTestCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_es.account_chart_template_full', edi_format_ref='l10n_es_edi_sii.edi_es_sii'):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
cls.frozen_today = datetime(year=2019, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone('utc'))
# Allow to see the full result of AssertionError.
cls.maxDiff = None
# ==== Config ====
cls.certificate = cls.env['l10n_es_edi.certificate'].create({
'content': base64.encodebytes(
misc.file_open("l10n_es_edi_sii/demo/certificates/aeat_1234.p12", 'rb').read()),
'password': '1234',
})
cls.company_data['company'].write({
'country_id': cls.env.ref('base.es').id,
'state_id': cls.env.ref('base.state_es_z').id,
'l10n_es_edi_certificate_id': cls.certificate.id,
'vat': 'ES59962470K',
'l10n_es_edi_test_env': True,
'l10n_es_edi_tax_agency': 'bizkaia',
})
# To be sure it is put by default on purchase journals as well (tbai module)
cls.company_data['default_journal_purchase'].write({
'edi_format_ids': [(6, 0, cls.edi_format.ids)],
})
# ==== Business ====
cls.partner_a.write({
'vat': 'BE0477472701',
'country_id': cls.env.ref('base.be').id,
})
cls.partner_b.write({
'vat': 'ESF35999705',
})
cls.product_t = cls.env["product.product"].create(
{"name": "Test product"})
cls.partner_t = cls.env["res.partner"].create({"name": "Test partner", "vat": "ESF35999705"})
@classmethod
def _get_tax_by_xml_id(cls, trailing_xml_id):
""" Helper to retrieve a tax easily.
:param trailing_xml_id: The trailing tax's xml id.
:return: An account.tax record
"""
return cls.env.ref(f'l10n_es.{cls.env.company.id}_account_tax_template_{trailing_xml_id}')
@classmethod
def create_invoice(cls, **kwargs):
return cls.env['account.move'].with_context(edi_test_mode=True).create({
'move_type': 'out_invoice',
'partner_id': cls.partner_a.id,
'invoice_date': '2019-01-01',
'date': '2019-01-01',
**kwargs,
'invoice_line_ids': [(0, 0, {
'product_id': cls.product_a.id,
'price_unit': 1000.0,
**line_vals,
}) for line_vals in kwargs.get('invoice_line_ids', [])],
})

View file

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from odoo.tests import tagged
from odoo import fields
from .common import TestEsEdiCommon
@tagged('external_l10n', 'post_install', '-at_install', '-standard', 'external')
class TestEdiWebServices(TestEsEdiCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_es.account_chart_template_full', edi_format_ref='l10n_es_edi_sii.edi_es_sii'):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
# Invoice name are tracked by the web-services so this constant tries to get a new unique invoice name at each
# execution.
cls.today = datetime.now()
cls.time_name = cls.today.strftime('%H%M%S')
cls.out_invoice = cls.env['account.move'].create({
'name': f'INV{cls.time_name}',
'move_type': 'out_invoice',
'partner_id': cls.partner_a.id,
'invoice_line_ids': [(0, 0, {
'product_id': cls.product_a.id,
'price_unit': 1000.0,
'quantity': 5,
'discount': 20.0,
'tax_ids': [(6, 0, cls._get_tax_by_xml_id('s_iva21b').ids)],
})],
})
cls.out_invoice.action_post()
cls.in_invoice = cls.env['account.move'].create({
'name': f'BILL{cls.time_name}',
'ref': f'REFBILL{cls.time_name}',
'move_type': 'in_invoice',
'partner_id': cls.partner_a.id,
'invoice_date': fields.Date.to_string(cls.today.date()),
'invoice_line_ids': [(0, 0, {
'product_id': cls.product_a.id,
'price_unit': 1000.0,
'quantity': 5,
'discount': 20.0,
'tax_ids': [(6, 0, cls._get_tax_by_xml_id('p_iva10_bc').ids)],
})],
})
cls.in_invoice.action_post()
cls.moves = cls.out_invoice + cls.in_invoice
def test_edi_gipuzkoa(self):
self.env.company.l10n_es_edi_tax_agency = 'gipuzkoa'
self.moves.action_process_edi_web_services(with_commit=False)
generated_files = self._process_documents_web_services(self.moves, {'es_sii'})
self.assertTrue(generated_files)
self.assertRecordValues(self.out_invoice, [{'edi_state': 'sent'}])
self.assertRecordValues(self.in_invoice, [{'edi_state': 'sent'}])
def test_edi_bizkaia(self):
self.env.company.l10n_es_edi_tax_agency = 'bizkaia'
self.moves.action_process_edi_web_services(with_commit=False)
generated_files = self._process_documents_web_services(self.moves, {'es_sii'})
self.assertTrue(generated_files)
self.assertRecordValues(self.out_invoice, [{'edi_state': 'sent'}])
self.assertRecordValues(self.in_invoice, [{'edi_state': 'sent'}])

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
from datetime import date
from freezegun import freeze_time
from odoo.exceptions import UserError
from odoo.tests import Form, tagged
from .common import TestEsEdiCommon, mocked_l10n_es_edi_call_web_service_sign
@tagged("post_install_l10n", "post_install", "-at_install")
class TestResequenceSII(TestEsEdiCommon):
@classmethod
def setUpClass(
cls,
chart_template_ref="l10n_es.account_chart_template_full",
edi_format_ref="l10n_es_edi_sii.edi_es_sii",
):
cls.startClassPatcher(freeze_time("2019-06-01", tick=True))
super().setUpClass(
chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref
)
cls.classPatch(
cls.registry["account.edi.format"],
"_l10n_es_edi_call_web_service_sign",
mocked_l10n_es_edi_call_web_service_sign,
)
# Create 2 customer and 2 vendor invoices, in wrong date order
cls.customer_invoice_2 = cls.create_invoice(
invoice_date="2019-05-15", date="2019-05-15", invoice_line_ids=[{}]
)
cls.customer_invoice_1 = cls.create_invoice(
invoice_date="2019-05-01", date="2019-05-01", invoice_line_ids=[{}]
)
cls.vendor_invoice_2 = cls.create_invoice(
invoice_date="2019-04-15",
date="2019-04-15",
move_type="in_invoice",
ref="vendor/1",
invoice_line_ids=[{}],
)
cls.vendor_invoice_1 = cls.create_invoice(
invoice_date="2019-04-01",
date="2019-04-01",
move_type="in_invoice",
ref="vendor/2",
invoice_line_ids=[{}],
)
# Post them, in wrong date order
cls.customer_invoice_2.action_post()
cls.customer_invoice_1.action_post()
cls.vendor_invoice_2.action_post()
cls.vendor_invoice_1.action_post()
def setUp(self):
super().setUp()
# Send to SII
all_invoices = (
self.customer_invoice_1
+ self.customer_invoice_2
+ self.vendor_invoice_1
+ self.vendor_invoice_2
)
self.generated_files = self._process_documents_web_services(
all_invoices, {self.edi_format.code}
)
self.assertRecordValues(
all_invoices,
[
{
"invoice_date": date(2019, 5, 1),
"date": date(2019, 5, 1),
"name": "INV/2019/00002",
},
{
"invoice_date": date(2019, 5, 15),
"date": date(2019, 5, 15),
"name": "INV/2019/00001",
},
{
"invoice_date": date(2019, 4, 1),
"date": date(2019, 4, 1),
"name": "BILL/2019/04/0002",
},
{
"invoice_date": date(2019, 4, 15),
"date": date(2019, 4, 15),
"name": "BILL/2019/04/0001",
},
],
)
def test_customer_fails(self):
"""Check we cannot resequence customer invoices."""
invoices = self.customer_invoice_1 + self.customer_invoice_2
wiz_f = Form(
self.env["account.resequence.wizard"].with_context(
active_model="account.move",
active_ids=invoices.ids,
)
)
wiz_f.ordering = "date"
wiz = wiz_f.save()
with self.assertRaises(
UserError,
msg="The following documents have already been sent and cannot be resequenced: INV/2019/00001, INV/2019/00002",
):
wiz.resequence()
def test_vendor_works(self):
"""Check we can resequence vendor bills."""
invoices = self.vendor_invoice_1 + self.vendor_invoice_2
wiz_f = Form(
self.env["account.resequence.wizard"].with_context(
active_model="account.move",
active_ids=invoices.ids,
)
)
wiz_f.ordering = "date"
wiz = wiz_f.save()
wiz.resequence()
self.assertRecordValues(
invoices,
[
{
"invoice_date": date(2019, 4, 1),
"date": date(2019, 4, 1),
"name": "BILL/2019/04/0001",
},
{
"invoice_date": date(2019, 4, 15),
"date": date(2019, 4, 15),
"name": "BILL/2019/04/0002",
},
],
)

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_move_form_inherit_l10n_es_edi" model="ir.ui.view">
<field name="name">account.move.form.inherit.l10n_es_edi</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@id='other_tab_group']//group[@name='accounting_info_group']" position="inside">
<field name="l10n_es_registration_date" attrs="{'readonly': [('state', '=', 'posted')], 'invisible': [('country_code', '!=', 'ES')]}"/>
<field name="l10n_es_edi_csv" invisible="1"/>
</xpath>
<field name="ref" position="attributes">
<attribute name="attrs">{'readonly': [('l10n_es_edi_csv', '!=', False)]}</attribute>
</field>
</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="account_tax_form_inherit_l10n_es_edi" model="ir.ui.view">
<field name="name">account.tax.form.inherit.l10n_es_edi</field>
<field name="model">account.tax</field>
<field name="inherit_id" ref="account.view_tax_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='tax_scope']" position="after">
<field name="l10n_es_type"
attrs="{'invisible': [('country_code', '!=', 'ES')]}"/>
<field name="l10n_es_exempt_reason"
attrs="{'invisible': [('country_code', '!=', 'ES'), ('l10n_es_type', '!=', 'exento')], 'required': [('l10n_es_type', '=', 'exento'), ('type_tax_use', '=', 'sale')]}"/>
<field name="l10n_es_bien_inversion"
attrs="{'invisible': [('country_code', '!=', 'ES'), ('l10n_es_type', '!=', 'exento')], 'required': [('l10n_es_type', '=', 'exento'), ('type_tax_use', '=', 'sale')]}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="l10n_es_edi_certificate_form" model="ir.ui.view">
<field name="name">l10n_es_edi.certificate.form</field>
<field name="model">l10n_es_edi.certificate</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="content"/>
<field name="password" password="True"/>
<label for="date_start" string="Validity"/>
<div>
<field name="date_start"/> -
<field name="date_end"/>
</div>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="l10n_es_edi_certificate_tree" model="ir.ui.view">
<field name="name">l10n_es_edi.certificate.tree</field>
<field name="model">l10n_es_edi.certificate</field>
<field name="arch" type="xml">
<tree>
<field name="date_start"/>
<field name="date_end"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="l10n_es_edi_certificate_action" model="ir.actions.act_window">
<field name="name">Certificates for EDI invoices on Spain</field>
<field name="res_model">l10n_es_edi.certificate</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first certificate</p>
</field>
</record>
<menuitem id="menu_l10n_es_edi_root"
name="Spain"
sequence="110"
groups="account.group_account_manager"
parent="account.menu_finance_configuration">
<menuitem id="menu_l10n_es_edi_certificates"
name="Certificates (ES)"
action="l10n_es_edi_certificate_action"
sequence="100"
groups="account.group_account_manager"/>
</menuitem>
</data>
</odoo>

View file

@ -0,0 +1,59 @@
<?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.es</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@data-key='account']/div" position="after">
<h2 attrs="{'invisible': [('country_code', '!=', 'ES')]}">Spain Localization</h2>
<div class="row mt16 o_settings_container"
name="spain_localization"
attrs="{'invisible': [('country_code', '!=', 'ES')]}">
<div class="col-xs-12 col-md-6 o_setting_box">
<!-- Invisible fields -->
<field name="l10n_es_edi_certificate_ids" invisible="1"/>
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<span class="o_form_label">Registro de Libros connection SII</span>
<span class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
groups="base.group_multi_company"/>
<div class="content-group">
<div class="mt16">
<label for="l10n_es_edi_tax_agency" class="o_light_label"/>
<field name="l10n_es_edi_tax_agency"/>
<div class="text-muted" attrs="{'invisible': [('l10n_es_edi_tax_agency', '!=', False)]}">
No tax agency selected: SII not activated.
</div>
<div class="text-muted" attrs="{'invisible': [('l10n_es_edi_tax_agency', '=', False)]}">
Tax agency selected: invoices will be sent by SII for journals where it is activated.
</div>
<br/>
<div class="o_row">
<label for="l10n_es_edi_test_env" class="o_light_label"/>
<field name="l10n_es_edi_test_env"/>
</div>
<div class="text-muted" attrs="{'invisible': [('l10n_es_edi_test_env', '=', False)]}">
Test mode: EDI data is sent to separate test servers and is not considered official.
</div>
<div class="text-muted" attrs="{'invisible': [('l10n_es_edi_test_env', '!=', False)]}">
Production mode: EDI data is sent to the official agency servers.
</div>
<br/>
<div>
<button name="%(l10n_es_edi_certificate_action)d" type="action" class="oe_link">Manage certificates (SII/TicketBAI)</button>
</div>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,2 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_resequence_wizard

View file

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class AccountResequenceWizard(models.TransientModel):
_inherit = "account.resequence.wizard"
def _frozen_edi_documents(self):
docs = super()._frozen_edi_documents()
# SII vendor bills are sent with ref, so they can be resequenced
return docs.filtered(
lambda doc: doc.edi_format_id.code != "es_sii"
or doc.move_id.is_sale_document()
)

View file

@ -0,0 +1,43 @@
[project]
name = "odoo-bringout-oca-ocb-l10n_es_edi_sii"
version = "16.0.0"
description = "Spain - SII EDI Suministro de Libros - Odoo addon"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-l10n_es>=16.0.0",
"odoo-bringout-oca-ocb-account_edi>=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_es_edi_sii"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]