odoo-addon-l10n-es-verifactu-oca 16.0.1.0.0.22__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.
Potentially problematic release.
This version of odoo-addon-l10n-es-verifactu-oca might be problematic. Click here for more details.
- odoo/addons/l10n_es_verifactu_oca/README.rst +151 -0
- odoo/addons/l10n_es_verifactu_oca/__init__.py +2 -0
- odoo/addons/l10n_es_verifactu_oca/__manifest__.py +43 -0
- odoo/addons/l10n_es_verifactu_oca/data/account_fiscal_position_template_data.xml +149 -0
- odoo/addons/l10n_es_verifactu_oca/data/ir_config_parameter.xml +9 -0
- odoo/addons/l10n_es_verifactu_oca/data/ir_cron.xml +14 -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/verifactu_map_data.xml +128 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu_registration_key_data.xml +207 -0
- odoo/addons/l10n_es_verifactu_oca/data/verifactu_tax_agency_data.xml +19 -0
- odoo/addons/l10n_es_verifactu_oca/i18n/es.po +1640 -0
- odoo/addons/l10n_es_verifactu_oca/i18n/l10n_es_verifactu_oca.pot +1598 -0
- odoo/addons/l10n_es_verifactu_oca/models/__init__.py +15 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_fiscal_position.py +34 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_fiscal_position_template.py +18 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_journal.py +64 -0
- odoo/addons/l10n_es_verifactu_oca/models/account_move.py +558 -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 +30 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_developer.py +16 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry.py +378 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry_response.py +122 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_invoice_entry_response_line.py +35 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_map.py +66 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_mixin.py +449 -0
- odoo/addons/l10n_es_verifactu_oca/models/verifactu_registration_key.py +24 -0
- odoo/addons/l10n_es_verifactu_oca/readme/CONFIGURE.rst +18 -0
- odoo/addons/l10n_es_verifactu_oca/readme/CONTRIBUTORS.rst +16 -0
- odoo/addons/l10n_es_verifactu_oca/readme/DESCRIPTION.rst +1 -0
- odoo/addons/l10n_es_verifactu_oca/readme/INSTALL.rst +4 -0
- odoo/addons/l10n_es_verifactu_oca/readme/ROADMAP.rst +15 -0
- odoo/addons/l10n_es_verifactu_oca/readme/USAGE.rst +1 -0
- odoo/addons/l10n_es_verifactu_oca/security/ir.model.access.csv +22 -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 +505 -0
- odoo/addons/l10n_es_verifactu_oca/tests/__init__.py +2 -0
- odoo/addons/l10n_es_verifactu_oca/tests/common.py +276 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_1.json +35 -0
- odoo/addons/l10n_es_verifactu_oca/tests/json/verifactu_mocked_response_2.json +35 -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 +392 -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 +30 -0
- odoo/addons/l10n_es_verifactu_oca/views/account_journal_view.xml +28 -0
- odoo/addons/l10n_es_verifactu_oca/views/account_move_view.xml +219 -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 +26 -0
- odoo/addons/l10n_es_verifactu_oca/views/res_company_view.xml +50 -0
- odoo/addons/l10n_es_verifactu_oca/views/res_partner_view.xml +14 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_chaining_view.xml +47 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_developer_view.xml +48 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_invoice_entry_response_view.xml +149 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_invoice_entry_view.xml +124 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_map_lines_view.xml +20 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_map_view.xml +53 -0
- odoo/addons/l10n_es_verifactu_oca/views/verifactu_registration_keys_view.xml +42 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/__init__.py +1 -0
- odoo/addons/l10n_es_verifactu_oca/wizards/account_move_reversal.py +16 -0
- odoo_addon_l10n_es_verifactu_oca-16.0.1.0.0.22.dist-info/METADATA +168 -0
- odoo_addon_l10n_es_verifactu_oca-16.0.1.0.0.22.dist-info/RECORD +68 -0
- odoo_addon_l10n_es_verifactu_oca-16.0.1.0.0.22.dist-info/WHEEL +5 -0
- odoo_addon_l10n_es_verifactu_oca-16.0.1.0.0.22.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# Copyright 2024 Aures TIC - Almudena de La Puente
|
|
2
|
+
# Copyright 2024 FactorLibre - Luis J. Salvatierra
|
|
3
|
+
# Copyright 2025 ForgeFlow S.L.
|
|
4
|
+
# Copyright 2025 Process Control - Jorge Luis López
|
|
5
|
+
# Copyright 2025 Tecnativa - Pedro M. Baeza
|
|
6
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
|
7
|
+
import json
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from hashlib import sha256
|
|
10
|
+
from unittest.mock import MagicMock, patch
|
|
11
|
+
from urllib.parse import parse_qs, urlparse
|
|
12
|
+
|
|
13
|
+
from freezegun import freeze_time
|
|
14
|
+
|
|
15
|
+
from odoo import Command
|
|
16
|
+
from odoo.exceptions import UserError
|
|
17
|
+
from odoo.modules.module import get_resource_path
|
|
18
|
+
|
|
19
|
+
from .common import TestVerifactuCommon
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestL10nEsAeatVerifactu(TestVerifactuCommon):
|
|
23
|
+
def test_verifactu_hash_code(self):
|
|
24
|
+
# based on AEAT VERI*FACTU documentation
|
|
25
|
+
# https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Veri-Factu_especificaciones_huella_hash_registros.pdf # noqa: B950
|
|
26
|
+
expected_hash = (
|
|
27
|
+
"6FA5B3FA912C71B23C274952AA00E13A5F40F0CEE466640FFAAD041FA8B79BFF"
|
|
28
|
+
)
|
|
29
|
+
verifactu_hash_string = (
|
|
30
|
+
"IDEmisorFactura=89890001K&"
|
|
31
|
+
"NumSerieFactura=12345678/G33&"
|
|
32
|
+
"FechaExpedicionFactura=01-01-2026&"
|
|
33
|
+
"TipoFactura=F1&"
|
|
34
|
+
"CuotaTotal=12.35&"
|
|
35
|
+
"ImporteTotal=123.45&"
|
|
36
|
+
"Huella=&"
|
|
37
|
+
"FechaHoraHusoGenRegistro=2026-01-01T19:20:30+01:00"
|
|
38
|
+
)
|
|
39
|
+
sha_hash_code = sha256(verifactu_hash_string.encode("utf-8"))
|
|
40
|
+
hash_code = sha_hash_code.hexdigest().upper()
|
|
41
|
+
self.assertEqual(hash_code, expected_hash)
|
|
42
|
+
|
|
43
|
+
def _create_and_test_invoice_verifactu_dict(
|
|
44
|
+
self, name, inv_type, lines, extra_vals, module=None
|
|
45
|
+
):
|
|
46
|
+
vals = []
|
|
47
|
+
tax_names = []
|
|
48
|
+
for line in lines:
|
|
49
|
+
taxes = self.env["account.tax"]
|
|
50
|
+
for tax in line[1]:
|
|
51
|
+
if "." in tax:
|
|
52
|
+
xml_id = tax
|
|
53
|
+
else:
|
|
54
|
+
xml_id = "l10n_es.{}_account_tax_template_{}".format(
|
|
55
|
+
self.company.id, tax
|
|
56
|
+
)
|
|
57
|
+
taxes += self.env.ref(xml_id)
|
|
58
|
+
tax_names.append(tax)
|
|
59
|
+
vals.append({"price_unit": line[0], "taxes": taxes})
|
|
60
|
+
return self._compare_verifactu_dict(
|
|
61
|
+
"verifactu_{}_{}_dict.json".format(inv_type, "_".join(tax_names)),
|
|
62
|
+
name,
|
|
63
|
+
inv_type,
|
|
64
|
+
vals,
|
|
65
|
+
extra_vals=extra_vals,
|
|
66
|
+
module=module,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _compare_verifactu_dict(
|
|
70
|
+
self, json_file, name, inv_type, lines, extra_vals=None, module=None
|
|
71
|
+
):
|
|
72
|
+
"""Helper method for creating an invoice according arguments, and
|
|
73
|
+
comparing the expected verifactu dict with .
|
|
74
|
+
"""
|
|
75
|
+
module = module or "l10n_es_verifactu_oca"
|
|
76
|
+
vals = {
|
|
77
|
+
"name": name,
|
|
78
|
+
"partner_id": self.partner.id,
|
|
79
|
+
"invoice_date": "2026-01-01",
|
|
80
|
+
"move_type": inv_type,
|
|
81
|
+
"invoice_line_ids": [],
|
|
82
|
+
}
|
|
83
|
+
for line in lines:
|
|
84
|
+
vals["invoice_line_ids"].append(
|
|
85
|
+
Command.create(
|
|
86
|
+
{
|
|
87
|
+
"product_id": self.product.id,
|
|
88
|
+
"account_id": self.account_expense.id,
|
|
89
|
+
"name": "Test line",
|
|
90
|
+
"price_unit": line["price_unit"],
|
|
91
|
+
"quantity": 1,
|
|
92
|
+
"tax_ids": [(6, 0, line["taxes"].ids)],
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
if extra_vals:
|
|
97
|
+
vals.update(extra_vals)
|
|
98
|
+
invoice = self.env["account.move"].create(vals)
|
|
99
|
+
self._activate_certificate(self.certificate_password)
|
|
100
|
+
first_now = datetime(2026, 1, 1, 19, 20, 30)
|
|
101
|
+
with patch.object(self.env.cr, "now", lambda: first_now), freeze_time(
|
|
102
|
+
first_now
|
|
103
|
+
):
|
|
104
|
+
invoice.action_post()
|
|
105
|
+
result_dict = invoice._get_verifactu_invoice_dict()
|
|
106
|
+
result_dict["RegistroAlta"].pop("FechaHoraHusoGenRegistro")
|
|
107
|
+
result_dict["RegistroAlta"].pop("TipoHuella")
|
|
108
|
+
result_dict["RegistroAlta"].pop("Huella")
|
|
109
|
+
path = get_resource_path(module, "tests/json", json_file)
|
|
110
|
+
if not path:
|
|
111
|
+
raise Exception("Incorrect JSON file: %s" % json_file)
|
|
112
|
+
with open(path, "r") as f:
|
|
113
|
+
expected_dict = json.loads(f.read())
|
|
114
|
+
self.assertEqual(expected_dict, result_dict)
|
|
115
|
+
entry = invoice.last_verifactu_invoice_entry_id
|
|
116
|
+
# Verify integration workflow
|
|
117
|
+
self.assertTrue(entry, "Invoice should have verifactu entry")
|
|
118
|
+
self.assertTrue(entry.aeat_json_data, "Should have JSON data")
|
|
119
|
+
return invoice
|
|
120
|
+
|
|
121
|
+
def test_get_verifactu_invoice_data(self):
|
|
122
|
+
mapping = [
|
|
123
|
+
(
|
|
124
|
+
"TEST001",
|
|
125
|
+
"out_invoice",
|
|
126
|
+
[(100, ["s_iva10b"]), (200, ["s_iva21s"])],
|
|
127
|
+
{
|
|
128
|
+
"fiscal_position_id": self.fp_nacional.id,
|
|
129
|
+
"verifactu_registration_key": self.fp_registration_key_01.id,
|
|
130
|
+
"verifactu_registration_date": "2026-01-01 19:20:30",
|
|
131
|
+
},
|
|
132
|
+
),
|
|
133
|
+
(
|
|
134
|
+
"TEST002",
|
|
135
|
+
"out_refund",
|
|
136
|
+
[(100, ["s_iva10b"]), (100, ["s_iva10b"]), (200, ["s_iva21s"])],
|
|
137
|
+
{
|
|
138
|
+
"fiscal_position_id": self.fp_nacional.id,
|
|
139
|
+
"verifactu_registration_key": self.fp_registration_key_01.id,
|
|
140
|
+
"verifactu_registration_date": "2026-01-01 19:20:30",
|
|
141
|
+
},
|
|
142
|
+
),
|
|
143
|
+
(
|
|
144
|
+
"TEST003",
|
|
145
|
+
"out_invoice",
|
|
146
|
+
[(200, ["s_iva21s", "s_req52"])],
|
|
147
|
+
{
|
|
148
|
+
"fiscal_position_id": self.fp_recargo.id,
|
|
149
|
+
"verifactu_registration_key": self.fp_registration_key_01.id,
|
|
150
|
+
"verifactu_registration_date": "2026-01-01 19:20:30",
|
|
151
|
+
},
|
|
152
|
+
),
|
|
153
|
+
]
|
|
154
|
+
for name, inv_type, lines, extra_vals in mapping:
|
|
155
|
+
self._create_and_test_invoice_verifactu_dict(
|
|
156
|
+
name, inv_type, lines, extra_vals
|
|
157
|
+
)
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
def test_verifactu_start_date(self):
|
|
161
|
+
self.company.verifactu_start_date = "2018-01-01"
|
|
162
|
+
invoice1 = self.invoice.copy({"invoice_date": "2019-01-01"})
|
|
163
|
+
self.assertTrue(invoice1.verifactu_enabled)
|
|
164
|
+
invoice2 = self.invoice.copy({"invoice_date": "2017-01-01"})
|
|
165
|
+
invoice2.invoice_date = "2017-01-01"
|
|
166
|
+
self.assertFalse(invoice2.verifactu_enabled)
|
|
167
|
+
self.company.verifactu_start_date = False
|
|
168
|
+
self.assertTrue(invoice2.verifactu_enabled)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TestL10nEsAeatVerifactuQR(TestVerifactuCommon):
|
|
172
|
+
def _get_required_qr_params(self):
|
|
173
|
+
"""Helper to generate the required QR code parameters."""
|
|
174
|
+
return {
|
|
175
|
+
"nif": self.invoice.company_id.partner_id._parse_aeat_vat_info()[2],
|
|
176
|
+
"numserie": self.invoice.name,
|
|
177
|
+
"fecha": self.invoice._get_verifactu_date(self.invoice.invoice_date),
|
|
178
|
+
"importe": f"{self.invoice.amount_total:.2f}", # noqa
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
def test_verifactu_qr_generation(self):
|
|
182
|
+
"""
|
|
183
|
+
Test the generation of the QR code image for the invoice.
|
|
184
|
+
"""
|
|
185
|
+
self._activate_certificate(self.certificate_password)
|
|
186
|
+
self.invoice.action_post()
|
|
187
|
+
qr_code = self.invoice.verifactu_qr
|
|
188
|
+
self.assertTrue(qr_code, "QR code should be generated for the invoice.")
|
|
189
|
+
self.assertIsInstance(qr_code, bytes, "QR code should be in bytes format.")
|
|
190
|
+
|
|
191
|
+
def test_verifactu_qr_url_format(self):
|
|
192
|
+
"""
|
|
193
|
+
Test the format of the generated QR URL to ensure it meets expected criteria.
|
|
194
|
+
"""
|
|
195
|
+
self._activate_certificate(self.certificate_password)
|
|
196
|
+
self.invoice.action_post()
|
|
197
|
+
qr_url = self.invoice.verifactu_qr_url
|
|
198
|
+
self.assertTrue(qr_url, "QR URL should be generated for the invoice.")
|
|
199
|
+
test_url = self.env.ref(
|
|
200
|
+
"l10n_es_aeat.aeat_tax_agency_spain"
|
|
201
|
+
).verifactu_qr_base_url_test_address
|
|
202
|
+
self.assertTrue(test_url, "Test URL should not be empty.")
|
|
203
|
+
parsed_url = urlparse(qr_url)
|
|
204
|
+
actual_params = parse_qs(parsed_url.query)
|
|
205
|
+
expected_params = self._get_required_qr_params()
|
|
206
|
+
for key, expected_value in expected_params.items():
|
|
207
|
+
self.assertIn(
|
|
208
|
+
key, actual_params, f"QR URL should contain the parameter: {key}"
|
|
209
|
+
)
|
|
210
|
+
self.assertEqual(
|
|
211
|
+
actual_params[key][0],
|
|
212
|
+
str(expected_value),
|
|
213
|
+
f"QR URL parameter '{key}' should have value '{expected_value}', "
|
|
214
|
+
"got '{actual_params[key][0]}' instead.",
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def test_verifactu_qr_code_generation_on_draft(self):
|
|
218
|
+
"""
|
|
219
|
+
Ensure that the QR code is not generated for invoices in draft state.
|
|
220
|
+
"""
|
|
221
|
+
qr_code = self.invoice.verifactu_qr
|
|
222
|
+
self.assertFalse(qr_code, "QR code should not be generated for draft invoices.")
|
|
223
|
+
|
|
224
|
+
def test_verifactu_qr_code_after_update(self):
|
|
225
|
+
"""
|
|
226
|
+
Test that the QR code is regenerated if the invoice details are updated.
|
|
227
|
+
"""
|
|
228
|
+
self._activate_certificate(self.certificate_password)
|
|
229
|
+
self.invoice.action_post()
|
|
230
|
+
original_qr_code = self.invoice.verifactu_qr
|
|
231
|
+
with self.assertRaises(UserError):
|
|
232
|
+
self.invoice.button_cancel()
|
|
233
|
+
self.invoice.button_draft()
|
|
234
|
+
self.invoice.write(
|
|
235
|
+
{
|
|
236
|
+
"invoice_line_ids": [
|
|
237
|
+
Command.create(
|
|
238
|
+
{
|
|
239
|
+
"product_id": self.product.id,
|
|
240
|
+
"account_id": self.account_expense.id,
|
|
241
|
+
"name": "Updated line",
|
|
242
|
+
"price_unit": 200,
|
|
243
|
+
"quantity": 1,
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
self.invoice.action_post()
|
|
250
|
+
self.invoice.invalidate_model(["verifactu_qr_url", "verifactu_qr"])
|
|
251
|
+
updated_qr_code = self.invoice.verifactu_qr
|
|
252
|
+
self.assertNotEqual(
|
|
253
|
+
original_qr_code,
|
|
254
|
+
updated_qr_code,
|
|
255
|
+
"QR code should be regenerated after invoice update.",
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def test_send_invoices_to_verifactu(self):
|
|
259
|
+
self._activate_certificate(self.certificate_password)
|
|
260
|
+
self.invoice.action_post()
|
|
261
|
+
with patch(
|
|
262
|
+
"odoo.addons.l10n_es_verifactu_oca.models."
|
|
263
|
+
"verifactu_invoice_entry.VerifactuInvoiceEntry._connect_verifactu"
|
|
264
|
+
) as mock_connect:
|
|
265
|
+
mock_service = MagicMock()
|
|
266
|
+
module = "l10n_es_verifactu_oca"
|
|
267
|
+
json_file = "verifactu_mocked_response_1.json"
|
|
268
|
+
path = get_resource_path(module, "tests/json", json_file)
|
|
269
|
+
if not path:
|
|
270
|
+
raise Exception("Incorrect JSON file: %s" % json_file)
|
|
271
|
+
with open(path, "r") as f:
|
|
272
|
+
response_dict = json.loads(f.read())
|
|
273
|
+
mock_service.RegFactuSistemaFacturacion.return_value = response_dict
|
|
274
|
+
mock_connect.return_value = mock_service
|
|
275
|
+
# Execute the cron job to send the invoice to VERI*FACTU
|
|
276
|
+
self.env["verifactu.invoice.entry"]._cron_send_documents_to_verifactu()
|
|
277
|
+
self.assertEqual(
|
|
278
|
+
self.invoice.aeat_state,
|
|
279
|
+
"sent",
|
|
280
|
+
"Invoice should be marked as sent after VERI*FACTU processing.",
|
|
281
|
+
)
|
|
282
|
+
self.assertEqual(
|
|
283
|
+
self.invoice.verifactu_csv,
|
|
284
|
+
"A-Y23JP3582934",
|
|
285
|
+
"CSV should be generated correctly after sending to VERI*FACTU.",
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class TestVerifactuSendResponse(TestVerifactuCommon):
|
|
290
|
+
def test_create_activity_on_exception(self):
|
|
291
|
+
"""
|
|
292
|
+
Creates an activity whenever the connection with VERI*FACTU
|
|
293
|
+
is not possible.
|
|
294
|
+
"""
|
|
295
|
+
MailActivity = self.env["mail.activity"]
|
|
296
|
+
ActivityType = self.env.ref(
|
|
297
|
+
"l10n_es_verifactu_oca.mail_activity_data_exception"
|
|
298
|
+
)
|
|
299
|
+
# Send an invoice without a certificate
|
|
300
|
+
self.invoice.action_post()
|
|
301
|
+
self.env["verifactu.invoice.entry"]._cron_send_documents_to_verifactu()
|
|
302
|
+
self.assertEqual(self.invoice.aeat_state, "not_sent")
|
|
303
|
+
activity_1 = MailActivity.search(
|
|
304
|
+
[
|
|
305
|
+
("activity_type_id", "=", ActivityType.id),
|
|
306
|
+
("res_model", "=", "verifactu.invoice.entry.response"),
|
|
307
|
+
]
|
|
308
|
+
)
|
|
309
|
+
self.assertTrue(activity_1, "An exception activity should have been created")
|
|
310
|
+
self.invoice.resend_verifactu()
|
|
311
|
+
self.env["verifactu.invoice.entry"]._cron_send_documents_to_verifactu()
|
|
312
|
+
activity_2 = MailActivity.search(
|
|
313
|
+
[
|
|
314
|
+
("activity_type_id", "=", ActivityType.id),
|
|
315
|
+
("res_model", "=", "verifactu.invoice.entry.response"),
|
|
316
|
+
]
|
|
317
|
+
)
|
|
318
|
+
self.assertEqual(
|
|
319
|
+
len(activity_1),
|
|
320
|
+
len(activity_2),
|
|
321
|
+
"There should be only one exception activity created",
|
|
322
|
+
)
|
|
323
|
+
# Activate certificate and re-run the cron
|
|
324
|
+
self._activate_certificate(self.certificate_password)
|
|
325
|
+
self.env["verifactu.invoice.entry"]._cron_send_documents_to_verifactu()
|
|
326
|
+
activity_done = (
|
|
327
|
+
self.env["mail.activity"]
|
|
328
|
+
.with_context(active_test=False)
|
|
329
|
+
.search(
|
|
330
|
+
[
|
|
331
|
+
("activity_type_id", "=", ActivityType.id),
|
|
332
|
+
("res_model", "=", "verifactu.invoice.entry.response"),
|
|
333
|
+
]
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
# todo: fix this, it's not activity_done.has_recommended_activites,
|
|
337
|
+
# should check if it's not visible anymore to the user
|
|
338
|
+
self.assertFalse(
|
|
339
|
+
activity_done.has_recommended_activities,
|
|
340
|
+
"The exception activity should not appear.",
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def mock_verifactu_response(self, error_code, description):
|
|
344
|
+
"""Recreates a verifactu response"""
|
|
345
|
+
return {
|
|
346
|
+
"CSV": "dummy-csv",
|
|
347
|
+
"RespuestaLinea": [
|
|
348
|
+
{
|
|
349
|
+
"IDFactura": {
|
|
350
|
+
"NumSerieFactura": self.invoice.name,
|
|
351
|
+
},
|
|
352
|
+
"EstadoRegistro": "AceptadoConErrores",
|
|
353
|
+
"CodigoErrorRegistro": error_code,
|
|
354
|
+
"DescripcionErrorRegistro": description,
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@patch(
|
|
360
|
+
"odoo.addons.l10n_es_verifactu_oca.models.verifactu_invoice_entry."
|
|
361
|
+
"VerifactuInvoiceEntry._connect_verifactu"
|
|
362
|
+
)
|
|
363
|
+
def test_create_send_activity(self, mock_connect):
|
|
364
|
+
"""
|
|
365
|
+
Create an activity whenever the response from VERI*FACTU indicates
|
|
366
|
+
that incorrect invoices have been sent
|
|
367
|
+
"""
|
|
368
|
+
MailActivity = self.env["mail.activity"]
|
|
369
|
+
ActivityType = self.env.ref("mail.mail_activity_data_warning")
|
|
370
|
+
mock_service = MagicMock()
|
|
371
|
+
module = "l10n_es_verifactu_oca"
|
|
372
|
+
json_file = "verifactu_mocked_response_2.json"
|
|
373
|
+
path = get_resource_path(module, "tests/json", json_file)
|
|
374
|
+
if not path:
|
|
375
|
+
raise Exception("Incorrect JSON file: %s" % json_file)
|
|
376
|
+
with open(path, "r") as f:
|
|
377
|
+
response_dict = json.loads(f.read())
|
|
378
|
+
mock_service.RegFactuSistemaFacturacion.return_value = response_dict
|
|
379
|
+
mock_connect.return_value = mock_service
|
|
380
|
+
self.invoice.action_post()
|
|
381
|
+
self.env["verifactu.invoice.entry"]._cron_send_documents_to_verifactu()
|
|
382
|
+
activity = MailActivity.search(
|
|
383
|
+
[
|
|
384
|
+
("activity_type_id", "=", ActivityType.id),
|
|
385
|
+
("res_model", "=", "verifactu.invoice.entry.response"),
|
|
386
|
+
("summary", "=", "Check incorrect invoices from VERI*FACTU"),
|
|
387
|
+
]
|
|
388
|
+
)
|
|
389
|
+
self.assertTrue(
|
|
390
|
+
activity,
|
|
391
|
+
"A warning activity should be created for 'AceptadoConErrores' response",
|
|
392
|
+
)
|