odoo-addon-l10n-es-verifactu-oca 18.0.1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/l10n_es_verifactu_oca/README.rst +195 -0
- odoo/addons/l10n_es_verifactu_oca/__init__.py +3 -0
- odoo/addons/l10n_es_verifactu_oca/__manifest__.py +50 -0
- odoo/addons/l10n_es_verifactu_oca/data/ir_config_parameter.xml +9 -0
- odoo/addons/l10n_es_verifactu_oca/data/ir_cron.xml +13 -0
- odoo/addons/l10n_es_verifactu_oca/data/l10n.es.aeat.map.tax.line.tax.csv +50 -0
- odoo/addons/l10n_es_verifactu_oca/data/mail_activity_data.xml +11 -0
- odoo/addons/l10n_es_verifactu_oca/data/neutralize.sql +2 -0
- odoo/addons/l10n_es_verifactu_oca/data/template/account.fiscal.position-es_common.csv +27 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu.map.csv +2 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu.map.line.csv +8 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu_registration_key_data.xml +205 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu_tax_agency_data.xml +19 -0
- odoo/addons/l10n_es_verifactu_oca/hooks.py +43 -0
- odoo/addons/l10n_es_verifactu_oca/i18n/es.po +1682 -0
- odoo/addons/l10n_es_verifactu_oca/i18n/l10n_es_verifactu_oca.pot +1640 -0
- odoo/addons/l10n_es_verifactu_oca/migrations/18.0.1.1.0/pre-migration.py +25 -0
- odoo/addons/l10n_es_verifactu_oca/models/__init__.py +15 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_chart_template.py +17 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_fiscal_position.py +34 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_journal.py +64 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_move.py +631 -0
- odoo/addons/l10n_es_verifactu_oca/models/aeat_tax_agency.py +30 -0
- odoo/addons/l10n_es_verifactu_oca/models/res_company.py +48 -0
- odoo/addons/l10n_es_verifactu_oca/models/res_partner.py +26 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_chaining.py +37 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_developer.py +16 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry.py +398 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry_response.py +116 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry_response_line.py +47 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_map.py +68 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_mixin.py +485 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_registration_key.py +26 -0
- odoo/addons/l10n_es_verifactu_oca/readme/CONFIGURE.md +27 -0
- odoo/addons/l10n_es_verifactu_oca/readme/CONTRIBUTORS.md +18 -0
- odoo/addons/l10n_es_verifactu_oca/readme/DESCRIPTION.md +1 -0
- odoo/addons/l10n_es_verifactu_oca/readme/INSTALL.md +6 -0
- odoo/addons/l10n_es_verifactu_oca/readme/ROADMAP.md +30 -0
- odoo/addons/l10n_es_verifactu_oca/readme/USAGE.md +3 -0
- odoo/addons/l10n_es_verifactu_oca/security/ir.model.access.csv +23 -0
- odoo/addons/l10n_es_verifactu_oca/security/verifactu_security.xml +6 -0
- odoo/addons/l10n_es_verifactu_oca/static/description/icon.png +0 -0
- odoo/addons/l10n_es_verifactu_oca/static/description/index.html +551 -0
- odoo/addons/l10n_es_verifactu_oca/tests/__init__.py +2 -0
- odoo/addons/l10n_es_verifactu_oca/tests/common.py +281 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_accepted_with_errors.json +35 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_cancel.json +35 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_cancel_incorrect.json +37 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_cancel_with_errors.json +35 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_correct.json +35 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_duplicated.json +43 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_incorrect.json +37 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_out_invoice_s_iva10b_s_iva21s_dict.json +59 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_out_invoice_s_iva21s_s_req52_dict.json +58 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_out_refund_s_iva10b_s_iva10b_s_iva21s_dict.json +66 -0
- odoo/addons/l10n_es_verifactu_oca/tests/test_10n_es_verifactu.py +506 -0
- odoo/addons/l10n_es_verifactu_oca/tests/test_verifactu_invoice.py +348 -0
- odoo/addons/l10n_es_verifactu_oca/views/account_fiscal_position_view.xml +29 -0
- odoo/addons/l10n_es_verifactu_oca/views/account_journal_view.xml +22 -0
- odoo/addons/l10n_es_verifactu_oca/views/account_move_view.xml +237 -0
- odoo/addons/l10n_es_verifactu_oca/views/aeat_tax_agency_view.xml +31 -0
- odoo/addons/l10n_es_verifactu_oca/views/report_invoice.xml +53 -0
- odoo/addons/l10n_es_verifactu_oca/views/res_company_view.xml +50 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_chaining_view.xml +45 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_developer_view.xml +46 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_invoice_entry_response_view.xml +134 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_invoice_entry_view.xml +127 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_map_lines_view.xml +16 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_map_view.xml +54 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_registration_keys_view.xml +43 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/__init__.py +2 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/account_move_reversal.py +16 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/verifactu_cancel_invoice_wizard.py +24 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/verifactu_cancel_invoice_wizard_view.xml +35 -0
- odoo_addon_l10n_es_verifactu_oca-18.0.1.2.1.dist-info/METADATA +213 -0
- odoo_addon_l10n_es_verifactu_oca-18.0.1.2.1.dist-info/RECORD +78 -0
- odoo_addon_l10n_es_verifactu_oca-18.0.1.2.1.dist-info/WHEEL +5 -0
- odoo_addon_l10n_es_verifactu_oca-18.0.1.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Copyright 2024 Aures TIC - Almudena de La Puente <almudena@aurestic.es>
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
|
3
|
+
|
|
4
|
+
from datetime import date, timedelta
|
|
5
|
+
|
|
6
|
+
from .common import TestVerifactuCommon
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestVerifactuInvoice(TestVerifactuCommon):
|
|
10
|
+
"""Test class for VeriFactu Invoice functionality."""
|
|
11
|
+
|
|
12
|
+
def _generate_invoice_entry(self, invoice):
|
|
13
|
+
"""
|
|
14
|
+
Helper method to generate VERI*FACTU invoice entry for an invoice.
|
|
15
|
+
This assumes the invoice is already prepared for VERI*FACTU.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
invoice: Prepared invoice
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
verifactu.invoice: Created invoice entry
|
|
22
|
+
"""
|
|
23
|
+
invoice._generate_verifactu_chaining()
|
|
24
|
+
return invoice.last_verifactu_invoice_entry_id
|
|
25
|
+
|
|
26
|
+
def _create_invoice_sequence(
|
|
27
|
+
self, count=3, start_date="2024-01-01", company=None, amounts=None
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Helper method to create a sequence of invoices for invoice entry testing.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
count: Number of invoices to create
|
|
34
|
+
start_date: Starting date (format: "YYYY-MM-DD")
|
|
35
|
+
company: Company for invoices (defaults to self.company)
|
|
36
|
+
amounts: List of amounts for each invoice (defaults to 100, 110, 120, ...)
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
list: List of created and prepared invoices
|
|
40
|
+
"""
|
|
41
|
+
if amounts is None:
|
|
42
|
+
amounts = [100 + i * 10 for i in range(count)]
|
|
43
|
+
invoices = []
|
|
44
|
+
for i in range(count):
|
|
45
|
+
# Calculate date by adding days to start_date
|
|
46
|
+
year, month, day = map(int, start_date.split("-"))
|
|
47
|
+
invoice_date = (date(year, month, day) + timedelta(days=i)).strftime(
|
|
48
|
+
"%Y-%m-%d"
|
|
49
|
+
)
|
|
50
|
+
invoice = self._create_and_prepare_invoice(
|
|
51
|
+
company=company,
|
|
52
|
+
date=invoice_date,
|
|
53
|
+
amount=amounts[i] if i < len(amounts) else amounts[-1],
|
|
54
|
+
name=f"Chain test line {i + 1}",
|
|
55
|
+
)
|
|
56
|
+
invoices.append(invoice)
|
|
57
|
+
|
|
58
|
+
return invoices
|
|
59
|
+
|
|
60
|
+
def _verify_chain_integrity(self, chain_entries):
|
|
61
|
+
"""
|
|
62
|
+
Helper method to verify the integrity of a chain sequence.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
chain_entries: List of chain entries to verify
|
|
66
|
+
"""
|
|
67
|
+
for i, entry in enumerate(chain_entries):
|
|
68
|
+
if i == 0:
|
|
69
|
+
self.assertFalse(
|
|
70
|
+
entry.previous_invoice_entry_id,
|
|
71
|
+
"First entry should have no previous",
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
self.assertEqual(
|
|
75
|
+
entry.previous_invoice_entry_id,
|
|
76
|
+
chain_entries[i - 1],
|
|
77
|
+
f"Entry {i} should link to entry {i - 1}",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _clean_chain_entries(self, company=None):
|
|
81
|
+
"""
|
|
82
|
+
Helper method to clean all chain entries for a company.
|
|
83
|
+
Useful for test isolation.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
company: Company to clean entries for (defaults to self.company)
|
|
87
|
+
"""
|
|
88
|
+
if company is None:
|
|
89
|
+
company = self.company
|
|
90
|
+
|
|
91
|
+
self.env["verifactu.invoice.entry"].search(
|
|
92
|
+
[("company_id", "=", company.id)]
|
|
93
|
+
).unlink()
|
|
94
|
+
|
|
95
|
+
def _assert_chain_entry_properties(
|
|
96
|
+
self,
|
|
97
|
+
chain_entry,
|
|
98
|
+
expected_previous=None,
|
|
99
|
+
expected_document=None,
|
|
100
|
+
expected_company=None,
|
|
101
|
+
):
|
|
102
|
+
"""
|
|
103
|
+
Helper method to assert chain entry properties.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
chain_entry: Chain entry to verify
|
|
107
|
+
expected_previous: Expected previous chain entry (None for first entry)
|
|
108
|
+
expected_document: Expected document reference
|
|
109
|
+
expected_company: Expected company (defaults to self.company)
|
|
110
|
+
"""
|
|
111
|
+
if expected_company is None:
|
|
112
|
+
expected_company = self.company
|
|
113
|
+
if expected_previous is None:
|
|
114
|
+
self.assertFalse(
|
|
115
|
+
chain_entry.previous_invoice_entry_id,
|
|
116
|
+
"Chain entry should have no previous entry",
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
self.assertEqual(
|
|
120
|
+
chain_entry.previous_invoice_entry_id,
|
|
121
|
+
expected_previous,
|
|
122
|
+
"Chain entry should link to expected previous entry",
|
|
123
|
+
)
|
|
124
|
+
if expected_document:
|
|
125
|
+
self.assertEqual(
|
|
126
|
+
chain_entry.document,
|
|
127
|
+
expected_document,
|
|
128
|
+
"Chain entry should reference expected document",
|
|
129
|
+
)
|
|
130
|
+
self.assertEqual(
|
|
131
|
+
chain_entry.company_id,
|
|
132
|
+
expected_company,
|
|
133
|
+
"Chain entry should belong to expected company",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def test_verifactu_chain_first_invoice(self):
|
|
137
|
+
"""Test that the first invoice creates a chain entry."""
|
|
138
|
+
self._activate_certificate(self.certificate_password)
|
|
139
|
+
|
|
140
|
+
self._clean_chain_entries()
|
|
141
|
+
|
|
142
|
+
invoice = self._create_and_prepare_invoice()
|
|
143
|
+
chain_entry = self._generate_invoice_entry(invoice)
|
|
144
|
+
|
|
145
|
+
self.assertTrue(chain_entry, "Chain entry should be created")
|
|
146
|
+
|
|
147
|
+
self._assert_chain_entry_properties(
|
|
148
|
+
chain_entry, expected_previous=None, expected_document=invoice
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self.assertEqual(
|
|
152
|
+
chain_entry.document_hash,
|
|
153
|
+
invoice.verifactu_hash,
|
|
154
|
+
"Hash should match invoice hash",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def test_verifactu_chain_second_invoice(self):
|
|
158
|
+
"""Test that the second invoice creates a chain entry and links to previous."""
|
|
159
|
+
self._activate_certificate(self.certificate_password)
|
|
160
|
+
|
|
161
|
+
invoices = self._create_invoice_sequence(count=2, amounts=[100, 150])
|
|
162
|
+
|
|
163
|
+
first_chain_entry = self._generate_invoice_entry(invoices[0])
|
|
164
|
+
second_chain_entry = self._generate_invoice_entry(invoices[1])
|
|
165
|
+
|
|
166
|
+
self._assert_chain_entry_properties(
|
|
167
|
+
second_chain_entry,
|
|
168
|
+
expected_previous=first_chain_entry,
|
|
169
|
+
expected_document=invoices[1],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self.assertEqual(
|
|
173
|
+
second_chain_entry.previous_invoice_entry_id.document,
|
|
174
|
+
invoices[0],
|
|
175
|
+
"Previous document should be computed",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def test_verifactu_chain_multiple_companies_isolation(self):
|
|
179
|
+
"""Test that chains are isolated by company."""
|
|
180
|
+
self._activate_certificate(self.certificate_password)
|
|
181
|
+
|
|
182
|
+
second_company = self._create_test_company(
|
|
183
|
+
name="Test Company 2", vat="B29805314"
|
|
184
|
+
)
|
|
185
|
+
second_company.verifactu_chaining_id = self.env["verifactu.chaining"].create(
|
|
186
|
+
{
|
|
187
|
+
"name": "VERI*FACTU Chaining 2",
|
|
188
|
+
"sif_id": "12",
|
|
189
|
+
"installation_number": 2,
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
first_company_invoice = self._create_and_prepare_invoice()
|
|
193
|
+
first_company_entry = self._generate_invoice_entry(first_company_invoice)
|
|
194
|
+
second_company_invoice = self._create_and_prepare_invoice(
|
|
195
|
+
company=second_company, amount=200
|
|
196
|
+
)
|
|
197
|
+
second_company_entry = self._generate_invoice_entry(second_company_invoice)
|
|
198
|
+
self._assert_chain_entry_properties(first_company_entry, expected_previous=None)
|
|
199
|
+
self._assert_chain_entry_properties(
|
|
200
|
+
second_company_entry,
|
|
201
|
+
expected_previous=None,
|
|
202
|
+
expected_company=second_company,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def test_verifactu_chain_hash_includes_previous(self):
|
|
206
|
+
"""Test that hash calculation includes previous document hash."""
|
|
207
|
+
self._activate_certificate(self.certificate_password)
|
|
208
|
+
|
|
209
|
+
invoices = self._create_invoice_sequence(count=2)
|
|
210
|
+
|
|
211
|
+
self._generate_invoice_entry(invoices[0])
|
|
212
|
+
first_hash = invoices[0].verifactu_hash
|
|
213
|
+
|
|
214
|
+
self._generate_invoice_entry(invoices[1])
|
|
215
|
+
|
|
216
|
+
second_hash_string = invoices[1].verifactu_hash_string
|
|
217
|
+
self.assertIn(
|
|
218
|
+
first_hash,
|
|
219
|
+
second_hash_string,
|
|
220
|
+
"Second invoice hash should include first invoice hash",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def test_verifactu_chain_compute_document_name(self):
|
|
224
|
+
"""Test the document name computation."""
|
|
225
|
+
self._activate_certificate(self.certificate_password)
|
|
226
|
+
|
|
227
|
+
invoice = self._create_and_prepare_invoice()
|
|
228
|
+
chain_entry = self._generate_invoice_entry(invoice)
|
|
229
|
+
|
|
230
|
+
self.assertEqual(chain_entry.document, invoice)
|
|
231
|
+
|
|
232
|
+
empty_entry = self.env["verifactu.invoice.entry"].create(
|
|
233
|
+
{
|
|
234
|
+
"document_id": invoice.id,
|
|
235
|
+
"model": invoice._name,
|
|
236
|
+
"company_id": self.company.id,
|
|
237
|
+
"verifactu_chaining_id": self.company.verifactu_chaining_id.id,
|
|
238
|
+
"document_hash": "test_hash",
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
self.assertTrue(
|
|
242
|
+
empty_entry,
|
|
243
|
+
"Chain entry should be created even with non-existent document reference",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def test_verifactu_chain_next_document_linking(self):
|
|
247
|
+
"""Test that next document references are properly set."""
|
|
248
|
+
self._activate_certificate(self.certificate_password)
|
|
249
|
+
|
|
250
|
+
invoices = self._create_invoice_sequence(count=2, amounts=[100, 150])
|
|
251
|
+
|
|
252
|
+
entry_1 = self._generate_invoice_entry(invoices[0])
|
|
253
|
+
entry_2 = self._generate_invoice_entry(invoices[1])
|
|
254
|
+
|
|
255
|
+
self.assertEqual(
|
|
256
|
+
entry_1.document_id,
|
|
257
|
+
entry_2.previous_invoice_entry_id.document_id,
|
|
258
|
+
"First invoice should be the same as second entry in previous document",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def test_verifactu_chain_context_id_set(self):
|
|
262
|
+
"""Test that chain_context_id is properly set for invoices."""
|
|
263
|
+
self._activate_certificate(self.certificate_password)
|
|
264
|
+
|
|
265
|
+
invoice = self._create_and_prepare_invoice()
|
|
266
|
+
chain_entry = self._generate_invoice_entry(invoice)
|
|
267
|
+
|
|
268
|
+
self.assertEqual(
|
|
269
|
+
chain_entry.company_id, self.company, "Should be linked to company"
|
|
270
|
+
)
|
|
271
|
+
# Company relationship is already verified above
|
|
272
|
+
|
|
273
|
+
def test_verifactu_company_chaining(self):
|
|
274
|
+
"""Test that verifactu always uses company for chaining."""
|
|
275
|
+
self._activate_certificate(self.certificate_password)
|
|
276
|
+
|
|
277
|
+
invoice = self._create_and_prepare_invoice()
|
|
278
|
+
|
|
279
|
+
# Verify company has the required field for chaining
|
|
280
|
+
self.assertTrue(
|
|
281
|
+
hasattr(
|
|
282
|
+
self.company.verifactu_chaining_id, "last_verifactu_invoice_entry_id"
|
|
283
|
+
),
|
|
284
|
+
"VERI*FACTU chaining should have last_verifactu_invoice_entry_id field",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Verify invoice uses company for chaining
|
|
288
|
+
self.assertEqual(
|
|
289
|
+
invoice.company_id,
|
|
290
|
+
self.company,
|
|
291
|
+
"Invoice should use the correct company for chaining",
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def test_company_last_chain_entry_updated(self):
|
|
295
|
+
"""Test that company's last_verifactu_invoice_entry_id is updated."""
|
|
296
|
+
self._activate_certificate(self.certificate_password)
|
|
297
|
+
|
|
298
|
+
self.assertFalse(
|
|
299
|
+
self.company.verifactu_chaining_id.last_verifactu_invoice_entry_id,
|
|
300
|
+
"Company should initially have no last chain entry",
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
invoice1 = self._create_and_prepare_invoice(amount=100)
|
|
304
|
+
chain_entry1 = self._generate_invoice_entry(invoice1)
|
|
305
|
+
|
|
306
|
+
self.assertEqual(
|
|
307
|
+
self.company.verifactu_chaining_id.last_verifactu_invoice_entry_id,
|
|
308
|
+
chain_entry1,
|
|
309
|
+
"Company's last chain entry should be updated to first entry",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
invoice2 = self._create_and_prepare_invoice(amount=200)
|
|
313
|
+
chain_entry2 = self._generate_invoice_entry(invoice2)
|
|
314
|
+
|
|
315
|
+
self.assertEqual(
|
|
316
|
+
self.company.verifactu_chaining_id.last_verifactu_invoice_entry_id,
|
|
317
|
+
chain_entry2,
|
|
318
|
+
"Company's last chain entry should be updated to second entry",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
self.assertEqual(
|
|
322
|
+
chain_entry2.previous_invoice_entry_id,
|
|
323
|
+
chain_entry1,
|
|
324
|
+
"Second entry should reference first entry as previous",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def test_invoice_entry_creation(self):
|
|
328
|
+
"""Test the VERI*FACTU invoice entry creation."""
|
|
329
|
+
invoice_model = self.env["verifactu.invoice.entry"]
|
|
330
|
+
|
|
331
|
+
# Test creating a simple invoice entry
|
|
332
|
+
test_entry = invoice_model.create(
|
|
333
|
+
{
|
|
334
|
+
"document_id": "999999",
|
|
335
|
+
"model": "account.move", # Use a high ID that likely doesn't exist
|
|
336
|
+
"document_name": "Test Invoice Entry",
|
|
337
|
+
"company_id": self.company.id,
|
|
338
|
+
"verifactu_chaining_id": self.company.verifactu_chaining_id.id,
|
|
339
|
+
"document_hash": "test_hash_simple",
|
|
340
|
+
"aeat_json_data": '{"test": "data"}',
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
self.assertEqual(
|
|
345
|
+
test_entry.company_id,
|
|
346
|
+
self.company,
|
|
347
|
+
"Should be able to create chain entry with company context",
|
|
348
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!-- Copyright 2024 Aures TIC - Jose Zambudio <jose@aurestic.es>
|
|
3
|
+
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
|
4
|
+
<odoo>
|
|
5
|
+
<record id="view_account_position_form" model="ir.ui.view">
|
|
6
|
+
<field name="name">account.fiscal.position.form</field>
|
|
7
|
+
<field name="model">account.fiscal.position</field>
|
|
8
|
+
<field name="inherit_id" ref="account.view_account_position_form" />
|
|
9
|
+
<field name="arch" type="xml">
|
|
10
|
+
<xpath expr="//page[@name='account_mapping']" position="after">
|
|
11
|
+
<page
|
|
12
|
+
name="verifactu"
|
|
13
|
+
string="VERI*FACTU"
|
|
14
|
+
invisible="not verifactu_enabled"
|
|
15
|
+
>
|
|
16
|
+
<group>
|
|
17
|
+
<field name="aeat_active" string="Active?" />
|
|
18
|
+
<field name="verifactu_tax_key" string="Tax key" />
|
|
19
|
+
<field
|
|
20
|
+
name="verifactu_registration_key"
|
|
21
|
+
string="Registration key"
|
|
22
|
+
domain="[('verifactu_tax_key', '=', verifactu_tax_key)]"
|
|
23
|
+
/>
|
|
24
|
+
</group>
|
|
25
|
+
</page>
|
|
26
|
+
</xpath>
|
|
27
|
+
</field>
|
|
28
|
+
</record>
|
|
29
|
+
</odoo>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record id="view_account_journal_form_inherit" model="ir.ui.view">
|
|
4
|
+
<field name="name">account.journal.form - l10n_es_verifactu</field>
|
|
5
|
+
<field name="model">account.journal</field>
|
|
6
|
+
<field name="inherit_id" ref="account.view_account_journal_form" />
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<xpath
|
|
9
|
+
expr="//field[@name='restrict_mode_hash_table']"
|
|
10
|
+
position="attributes"
|
|
11
|
+
>
|
|
12
|
+
<attribute name="readonly">restrict_mode_hash_table_readonly</attribute>
|
|
13
|
+
</xpath>
|
|
14
|
+
<xpath expr="//field[@name='restrict_mode_hash_table']" position="before">
|
|
15
|
+
<field
|
|
16
|
+
name="verifactu_enabled"
|
|
17
|
+
invisible="not company_verifactu_enabled or type != 'sale'"
|
|
18
|
+
/>
|
|
19
|
+
</xpath>
|
|
20
|
+
</field>
|
|
21
|
+
</record>
|
|
22
|
+
</odoo>
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!-- Copyright 2024 Aures TIC - Jose Zambudio
|
|
3
|
+
Copyright 2024 Aures TIC - Almudena de La Puente
|
|
4
|
+
Copyright 2025 Tecnativa - Pedro M. Baeza
|
|
5
|
+
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
|
6
|
+
<odoo>
|
|
7
|
+
<record id="invoice_verifactu_form" model="ir.ui.view">
|
|
8
|
+
<field name="name">account.invoice.verifactu.form</field>
|
|
9
|
+
<field name="model">account.move</field>
|
|
10
|
+
<field name="inherit_id" ref="l10n_es_aeat.view_move_form" />
|
|
11
|
+
<field name="arch" type="xml">
|
|
12
|
+
<button name="button_draft" position="after">
|
|
13
|
+
<button
|
|
14
|
+
type="object"
|
|
15
|
+
string="Re-send to VERI*FACTU"
|
|
16
|
+
name="resend_verifactu"
|
|
17
|
+
groups="l10n_es_aeat.group_account_aeat"
|
|
18
|
+
invisible="not verifactu_enabled or state != 'posted' or aeat_state not in ('sent_w_errors', 'incorrect')"
|
|
19
|
+
/>
|
|
20
|
+
<button
|
|
21
|
+
type="object"
|
|
22
|
+
string="Cancel in VERI*FACTU"
|
|
23
|
+
name="cancel_verifactu"
|
|
24
|
+
groups="l10n_es_aeat.group_account_aeat"
|
|
25
|
+
invisible="not verifactu_enabled or state not in ('posted', 'cancel') or aeat_state not in ('sent_w_errors', 'cancel_w_errors', 'incorrect')"
|
|
26
|
+
/>
|
|
27
|
+
</button>
|
|
28
|
+
<notebook position="inside">
|
|
29
|
+
<page
|
|
30
|
+
string="VERI*FACTU"
|
|
31
|
+
name="page_aeat"
|
|
32
|
+
invisible="not verifactu_enabled"
|
|
33
|
+
>
|
|
34
|
+
<group>
|
|
35
|
+
<group
|
|
36
|
+
string="Configuration"
|
|
37
|
+
name="group_verifactu_configuration"
|
|
38
|
+
>
|
|
39
|
+
<field
|
|
40
|
+
name="verifactu_refund_type"
|
|
41
|
+
string="Refund type"
|
|
42
|
+
required="verifactu_enabled and move_type == 'out_refund'"
|
|
43
|
+
invisible="not verifactu_enabled or move_type != 'out_refund'"
|
|
44
|
+
/>
|
|
45
|
+
<field
|
|
46
|
+
name="verifactu_refund_specific_type"
|
|
47
|
+
string="Refund specific type"
|
|
48
|
+
invisible="not verifactu_enabled or move_type != 'out_refund'"
|
|
49
|
+
/>
|
|
50
|
+
<field
|
|
51
|
+
name="verifactu_tax_key"
|
|
52
|
+
string="Tax key"
|
|
53
|
+
required="verifactu_enabled"
|
|
54
|
+
readonly="aeat_state in ('sent', 'cancel')"
|
|
55
|
+
/>
|
|
56
|
+
<field
|
|
57
|
+
name="verifactu_registration_key"
|
|
58
|
+
string="Registration key"
|
|
59
|
+
required="verifactu_enabled"
|
|
60
|
+
readonly="aeat_state in ('sent', 'cancel')"
|
|
61
|
+
/>
|
|
62
|
+
<field name="verifactu_description" string="Description" />
|
|
63
|
+
<field
|
|
64
|
+
name="verifactu_cancel_reason"
|
|
65
|
+
invisible="state != 'cancel'"
|
|
66
|
+
/>
|
|
67
|
+
</group>
|
|
68
|
+
<group string="Information" name="group_verifactu_information">
|
|
69
|
+
<field
|
|
70
|
+
name="verifactu_hash_string"
|
|
71
|
+
string="Hash string"
|
|
72
|
+
readonly="1"
|
|
73
|
+
groups="base.group_no_one"
|
|
74
|
+
/>
|
|
75
|
+
<field
|
|
76
|
+
name="verifactu_hash"
|
|
77
|
+
string="Hash"
|
|
78
|
+
readonly="1"
|
|
79
|
+
groups="base.group_no_one"
|
|
80
|
+
/>
|
|
81
|
+
<field
|
|
82
|
+
name="verifactu_registration_date"
|
|
83
|
+
string="Registration date"
|
|
84
|
+
readonly="1"
|
|
85
|
+
/>
|
|
86
|
+
</group>
|
|
87
|
+
</group>
|
|
88
|
+
<group
|
|
89
|
+
string="Result"
|
|
90
|
+
name="group_verifactu_result"
|
|
91
|
+
groups="l10n_es_aeat.group_account_aeat"
|
|
92
|
+
>
|
|
93
|
+
<notebook colspan="2">
|
|
94
|
+
<page name="page_aeat_result_general" string="General">
|
|
95
|
+
<group>
|
|
96
|
+
<field name="aeat_state" string="Send State" />
|
|
97
|
+
<field name="verifactu_csv" string="CSV" />
|
|
98
|
+
<field
|
|
99
|
+
name="aeat_send_failed"
|
|
100
|
+
string="Failed?"
|
|
101
|
+
invisible="not aeat_send_failed"
|
|
102
|
+
/>
|
|
103
|
+
<field
|
|
104
|
+
name="aeat_send_error"
|
|
105
|
+
string="Send error"
|
|
106
|
+
invisible="not aeat_send_failed"
|
|
107
|
+
/>
|
|
108
|
+
</group>
|
|
109
|
+
</page>
|
|
110
|
+
<page
|
|
111
|
+
name="page_aeat_result_technical"
|
|
112
|
+
string="Technical"
|
|
113
|
+
groups="base.group_no_one"
|
|
114
|
+
>
|
|
115
|
+
<group>
|
|
116
|
+
<field
|
|
117
|
+
name="aeat_header_sent"
|
|
118
|
+
string="Sent header"
|
|
119
|
+
/>
|
|
120
|
+
<field
|
|
121
|
+
name="aeat_content_sent"
|
|
122
|
+
string="Sent content"
|
|
123
|
+
/>
|
|
124
|
+
</group>
|
|
125
|
+
<group string="Invoice entries">
|
|
126
|
+
<field
|
|
127
|
+
name="verifactu_invoice_entry_ids"
|
|
128
|
+
nolabel="1"
|
|
129
|
+
colspan="2"
|
|
130
|
+
/>
|
|
131
|
+
</group>
|
|
132
|
+
<group string="Responses">
|
|
133
|
+
<field
|
|
134
|
+
name="verifactu_response_line_ids"
|
|
135
|
+
nolabel="1"
|
|
136
|
+
colspan="2"
|
|
137
|
+
>
|
|
138
|
+
<list>
|
|
139
|
+
<field name="create_date" />
|
|
140
|
+
<field name="entry_id" />
|
|
141
|
+
<field
|
|
142
|
+
name="send_state"
|
|
143
|
+
widget="badge"
|
|
144
|
+
decoration-success="send_state == 'sent'"
|
|
145
|
+
decoration-danger="send_state == 'not_sent'"
|
|
146
|
+
decoration-warning="send_state in ('sent_w_errors', 'incorrect', 'cancel_incorrect', 'cancel_w_errors')"
|
|
147
|
+
decoration-muted="send_state == 'cancel'"
|
|
148
|
+
/>
|
|
149
|
+
</list>
|
|
150
|
+
</field>
|
|
151
|
+
</group>
|
|
152
|
+
</page>
|
|
153
|
+
</notebook>
|
|
154
|
+
</group>
|
|
155
|
+
</page>
|
|
156
|
+
</notebook>
|
|
157
|
+
</field>
|
|
158
|
+
</record>
|
|
159
|
+
|
|
160
|
+
<record id="invoice_verifactu_search" model="ir.ui.view">
|
|
161
|
+
<field name="name">account.invoice.verifactu.search</field>
|
|
162
|
+
<field name="model">account.move</field>
|
|
163
|
+
<field name="inherit_id" ref="account.view_account_invoice_filter" />
|
|
164
|
+
<field name="arch" type="xml">
|
|
165
|
+
<filter name="late" position="after">
|
|
166
|
+
<group string="VERI*FACTU filters">
|
|
167
|
+
<separator />
|
|
168
|
+
<filter
|
|
169
|
+
name="verifactu_not_sent"
|
|
170
|
+
string="VERI*FACTU not sent"
|
|
171
|
+
domain="[('aeat_state', '=', 'not_sent'), ('verifactu_enabled', '=', True)]"
|
|
172
|
+
help="Never sent to VERI*FACTU"
|
|
173
|
+
/>
|
|
174
|
+
<filter
|
|
175
|
+
name="verifactu_pending"
|
|
176
|
+
string="VERI*FACTU pending corrections"
|
|
177
|
+
domain="[('aeat_state', 'in', ['sent_w_errors','cancel_w_errors'])]"
|
|
178
|
+
help="Already sent but with corrections not sent yet"
|
|
179
|
+
/>
|
|
180
|
+
<filter
|
|
181
|
+
name="verifactu_sent"
|
|
182
|
+
string="VERI*FACTU sent"
|
|
183
|
+
domain="[('aeat_state', 'in', ('sent', 'cancel'))]"
|
|
184
|
+
help="Already sent to VERI*FACTU. It includes cancelled invoices"
|
|
185
|
+
/>
|
|
186
|
+
<filter
|
|
187
|
+
name="aeat_send_failed"
|
|
188
|
+
string="VERI*FACTU failed"
|
|
189
|
+
domain="[('aeat_send_failed', '=', True)]"
|
|
190
|
+
help="The last attemp to sent to VERI*FACTU has failed"
|
|
191
|
+
/>
|
|
192
|
+
<filter
|
|
193
|
+
name="verifactu_cancelled"
|
|
194
|
+
string="VERI*FACTU cancelled"
|
|
195
|
+
domain="[('aeat_state', '=', 'cancel')]"
|
|
196
|
+
help="Cancelled invoices at VERI*FACTU"
|
|
197
|
+
/>
|
|
198
|
+
<filter
|
|
199
|
+
name="verifactu_incorrect"
|
|
200
|
+
string="Incorrect send to VERI*FACTU"
|
|
201
|
+
domain="[('aeat_state', 'in', ['incorrect','cancel_incorrect'])]"
|
|
202
|
+
help="Incorrect send to VERI*FACTU"
|
|
203
|
+
/>
|
|
204
|
+
<separator />
|
|
205
|
+
</group>
|
|
206
|
+
</filter>
|
|
207
|
+
<xpath expr="//group" position="inside">
|
|
208
|
+
<separator />
|
|
209
|
+
<filter
|
|
210
|
+
string="VERI*FACTU error"
|
|
211
|
+
name="verifactu_error"
|
|
212
|
+
domain="[('aeat_send_failed', '=', True)]"
|
|
213
|
+
context="{'group_by':'aeat_send_error'}"
|
|
214
|
+
/>
|
|
215
|
+
</xpath>
|
|
216
|
+
</field>
|
|
217
|
+
</record>
|
|
218
|
+
|
|
219
|
+
<record id="invoice_verifactu_tree" model="ir.ui.view">
|
|
220
|
+
<field name="name">account.invoice.list</field>
|
|
221
|
+
<field name="model">account.move</field>
|
|
222
|
+
<field name="inherit_id" ref="account.view_invoice_tree" />
|
|
223
|
+
<field name="arch" type="xml">
|
|
224
|
+
<field name="status_in_payment" position="after">
|
|
225
|
+
<field
|
|
226
|
+
name="aeat_state"
|
|
227
|
+
decoration-success="aeat_state == 'sent'"
|
|
228
|
+
decoration-danger="aeat_state == 'not_sent'"
|
|
229
|
+
decoration-warning="aeat_state in ('sent_w_errors', 'incorrect', 'cancel_incorrect', 'cancel_w_errors')"
|
|
230
|
+
decoration-muted="aeat_state == 'cancel'"
|
|
231
|
+
widget="badge"
|
|
232
|
+
optional="hide"
|
|
233
|
+
/>
|
|
234
|
+
</field>
|
|
235
|
+
</field>
|
|
236
|
+
</record>
|
|
237
|
+
</odoo>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!-- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
|
3
|
+
<odoo>
|
|
4
|
+
<record id="verifactu_tax_agency_form_view" model="ir.ui.view">
|
|
5
|
+
<field name="name">aeat.tax.agency.form - l10n_es_verifactu</field>
|
|
6
|
+
<field name="model">aeat.tax.agency</field>
|
|
7
|
+
<field name="inherit_id" ref="l10n_es_aeat.aeat_tax_agency_form_view" />
|
|
8
|
+
<field name="arch" type="xml">
|
|
9
|
+
<notebook position="inside">
|
|
10
|
+
<page name="verifactu" string="VERI*FACTU">
|
|
11
|
+
<group>
|
|
12
|
+
<group string="Suministro Facturas Emitidas">
|
|
13
|
+
<field name="verifactu_wsdl_out" string="WSDL" />
|
|
14
|
+
<field
|
|
15
|
+
name="verifactu_wsdl_out_test_address"
|
|
16
|
+
string="Test Address"
|
|
17
|
+
/>
|
|
18
|
+
</group>
|
|
19
|
+
</group>
|
|
20
|
+
<group string="Validar QR">
|
|
21
|
+
<field name="verifactu_qr_base_url" string="Base URL" />
|
|
22
|
+
<field
|
|
23
|
+
name="verifactu_qr_base_url_test_address"
|
|
24
|
+
string="Test Base URL"
|
|
25
|
+
/>
|
|
26
|
+
</group>
|
|
27
|
+
</page>
|
|
28
|
+
</notebook>
|
|
29
|
+
</field>
|
|
30
|
+
</record>
|
|
31
|
+
</odoo>
|