odoo-addon-l10n-es-aeat-sii-oca 16.0.1.8.5.1__py3-none-any.whl → 16.0.2.0.0__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_aeat_sii_oca/README.rst +1 -1
- odoo/addons/l10n_es_aeat_sii_oca/__manifest__.py +1 -1
- odoo/addons/l10n_es_aeat_sii_oca/i18n/l10n_es_aeat_sii_oca.pot +42 -111
- odoo/addons/l10n_es_aeat_sii_oca/models/account_fiscal_position.py +0 -6
- odoo/addons/l10n_es_aeat_sii_oca/models/account_move.py +61 -60
- odoo/addons/l10n_es_aeat_sii_oca/models/res_company.py +1 -1
- odoo/addons/l10n_es_aeat_sii_oca/models/res_partner.py +8 -8
- odoo/addons/l10n_es_aeat_sii_oca/models/sii_mixin.py +72 -168
- odoo/addons/l10n_es_aeat_sii_oca/static/description/index.html +1 -1
- odoo/addons/l10n_es_aeat_sii_oca/tests/test_l10n_es_aeat_sii.py +14 -14
- odoo/addons/l10n_es_aeat_sii_oca/views/account_fiscal_position_view.xml +6 -6
- odoo/addons/l10n_es_aeat_sii_oca/views/account_move_views.xml +19 -19
- odoo/addons/l10n_es_aeat_sii_oca/views/res_partner_views.xml +2 -6
- {odoo_addon_l10n_es_aeat_sii_oca-16.0.1.8.5.1.dist-info → odoo_addon_l10n_es_aeat_sii_oca-16.0.2.0.0.dist-info}/METADATA +2 -2
- {odoo_addon_l10n_es_aeat_sii_oca-16.0.1.8.5.1.dist-info → odoo_addon_l10n_es_aeat_sii_oca-16.0.2.0.0.dist-info}/RECORD +17 -17
- {odoo_addon_l10n_es_aeat_sii_oca-16.0.1.8.5.1.dist-info → odoo_addon_l10n_es_aeat_sii_oca-16.0.2.0.0.dist-info}/WHEEL +0 -0
- {odoo_addon_l10n_es_aeat_sii_oca-16.0.1.8.5.1.dist-info → odoo_addon_l10n_es_aeat_sii_oca-16.0.2.0.0.dist-info}/top_level.txt +0 -0
@@ -7,26 +7,16 @@
|
|
7
7
|
import json
|
8
8
|
import logging
|
9
9
|
|
10
|
-
from requests import Session
|
11
|
-
|
12
10
|
from odoo import _, api, exceptions, fields, models
|
13
11
|
from odoo.exceptions import UserError, ValidationError
|
14
12
|
from odoo.modules.registry import Registry
|
15
13
|
from odoo.tools.float_utils import float_compare
|
16
14
|
|
17
|
-
|
15
|
+
from odoo.addons.l10n_es_aeat.models.aeat_mixin import round_by_keys
|
18
16
|
|
19
|
-
|
20
|
-
from zeep import Client
|
21
|
-
from zeep.plugins import HistoryPlugin
|
22
|
-
from zeep.transports import Transport
|
23
|
-
except (ImportError, IOError) as err:
|
24
|
-
_logger.debug(err)
|
17
|
+
_logger = logging.getLogger(__name__)
|
25
18
|
|
26
19
|
SII_STATES = [
|
27
|
-
("not_sent", "Not sent"),
|
28
|
-
("sent", "Sent"),
|
29
|
-
("sent_w_errors", "Accepted with errors"),
|
30
20
|
("sent_modified", "Registered in SII but last modifications not sent"),
|
31
21
|
("cancelled", "Cancelled"),
|
32
22
|
("cancelled_modified", "Cancelled in SII but last modifications not sent"),
|
@@ -36,24 +26,9 @@ SII_MACRODATA_LIMIT = 100000000.0
|
|
36
26
|
SII_DATE_FORMAT = "%d-%m-%Y"
|
37
27
|
|
38
28
|
|
39
|
-
def round_by_keys(elem, search_keys, prec=2):
|
40
|
-
"""This uses ``round`` method directly as if has been tested that Odoo's
|
41
|
-
``float_round`` still returns incorrect amounts for certain values. Try
|
42
|
-
3 units x 3,77 €/unit with 10% tax and you will be hit by the error
|
43
|
-
(on regular x86 architectures)."""
|
44
|
-
if isinstance(elem, dict):
|
45
|
-
for key, value in elem.items():
|
46
|
-
if key in search_keys:
|
47
|
-
elem[key] = round(elem[key], prec)
|
48
|
-
else:
|
49
|
-
round_by_keys(value, search_keys)
|
50
|
-
elif isinstance(elem, list):
|
51
|
-
for value in elem:
|
52
|
-
round_by_keys(value, search_keys)
|
53
|
-
|
54
|
-
|
55
29
|
class SiiMixin(models.AbstractModel):
|
56
30
|
_name = "sii.mixin"
|
31
|
+
_inherit = "aeat.mixin"
|
57
32
|
_description = "SII Mixin"
|
58
33
|
|
59
34
|
company_id = fields.Many2one(
|
@@ -68,38 +43,11 @@ class SiiMixin(models.AbstractModel):
|
|
68
43
|
readonly=False,
|
69
44
|
copy=False,
|
70
45
|
)
|
71
|
-
|
72
|
-
|
73
|
-
string="SII send state",
|
74
|
-
default="not_sent",
|
75
|
-
readonly=True,
|
76
|
-
copy=False,
|
77
|
-
help="Indicates the state of this document in relation with the "
|
78
|
-
"presentation at the SII",
|
46
|
+
aeat_state = fields.Selection(
|
47
|
+
selection_add=SII_STATES,
|
79
48
|
)
|
80
49
|
sii_csv = fields.Char(string="SII CSV", copy=False, readonly=True)
|
81
50
|
sii_return = fields.Text(string="SII Return", copy=False, readonly=True)
|
82
|
-
sii_header_sent = fields.Text(
|
83
|
-
string="SII last header sent",
|
84
|
-
copy=False,
|
85
|
-
readonly=True,
|
86
|
-
)
|
87
|
-
sii_content_sent = fields.Text(
|
88
|
-
string="SII last content sent",
|
89
|
-
copy=False,
|
90
|
-
readonly=True,
|
91
|
-
)
|
92
|
-
sii_send_error = fields.Text(
|
93
|
-
string="SII Send Error",
|
94
|
-
readonly=True,
|
95
|
-
copy=False,
|
96
|
-
)
|
97
|
-
sii_send_failed = fields.Boolean(
|
98
|
-
string="SII send failed",
|
99
|
-
copy=False,
|
100
|
-
help="Indicates that the last attempt to communicate this document to "
|
101
|
-
"the SII has failed. See SII return for details",
|
102
|
-
)
|
103
51
|
sii_refund_type = fields.Selection(
|
104
52
|
selection=[
|
105
53
|
# ('S', 'By substitution'), - Removed as not fully supported
|
@@ -214,17 +162,10 @@ class SiiMixin(models.AbstractModel):
|
|
214
162
|
>= 0
|
215
163
|
)
|
216
164
|
|
217
|
-
def _sii_get_partner(self):
|
218
|
-
raise NotImplementedError
|
219
|
-
|
220
|
-
def _get_sii_country_code(self):
|
221
|
-
self.ensure_one()
|
222
|
-
return self._sii_get_partner()._parse_aeat_vat_info()[0]
|
223
|
-
|
224
165
|
def _filter_sii_unlink_not_possible(self):
|
225
166
|
"""Filter records that we do not allow to be deleted, all those
|
226
167
|
that are not in not_sent sii status."""
|
227
|
-
return self.filtered(lambda rec: rec.
|
168
|
+
return self.filtered(lambda rec: rec.aeat_state != "not_sent")
|
228
169
|
|
229
170
|
@api.ondelete(at_uninstall=False)
|
230
171
|
def _unlink_except_sii(self):
|
@@ -235,7 +176,7 @@ class SiiMixin(models.AbstractModel):
|
|
235
176
|
)
|
236
177
|
|
237
178
|
@api.model
|
238
|
-
def
|
179
|
+
def _get_aeat_taxes_map(self, codes, date):
|
239
180
|
"""Return the codes that correspond to that sii map line codes.
|
240
181
|
|
241
182
|
:param codes: List of code strings to get the mapping.
|
@@ -257,12 +198,7 @@ class SiiMixin(models.AbstractModel):
|
|
257
198
|
tax_templates = sii_map.map_lines.filtered(lambda x: x.code in codes).taxes
|
258
199
|
return self.company_id.get_taxes_from_templates(tax_templates)
|
259
200
|
|
260
|
-
def
|
261
|
-
datetimeobject = fields.Date.to_date(date)
|
262
|
-
new_date = datetimeobject.strftime(SII_DATE_FORMAT)
|
263
|
-
return new_date
|
264
|
-
|
265
|
-
def _get_sii_header(self, tipo_comunicacion=False, cancellation=False):
|
201
|
+
def _get_aeat_header(self, tipo_comunicacion=False, cancellation=False):
|
266
202
|
"""Builds SII send header
|
267
203
|
|
268
204
|
:param tipo_comunicacion String 'A0': new reg, 'A1': modification
|
@@ -297,9 +233,6 @@ class SiiMixin(models.AbstractModel):
|
|
297
233
|
queue.unlink()
|
298
234
|
return True
|
299
235
|
|
300
|
-
def _get_valid_document_states(self):
|
301
|
-
raise NotImplementedError()
|
302
|
-
|
303
236
|
def send_sii(self):
|
304
237
|
"""General public method for filtering out of the starting recordset the records
|
305
238
|
that shouldn't be sent to the SII:
|
@@ -316,7 +249,7 @@ class SiiMixin(models.AbstractModel):
|
|
316
249
|
if (
|
317
250
|
not document.sii_enabled
|
318
251
|
or document.state not in valid_states
|
319
|
-
or document.
|
252
|
+
or document.aeat_state in ["sent", "cancelled"]
|
320
253
|
):
|
321
254
|
continue
|
322
255
|
job_states = document.sudo().mapped(jobs_field_name).mapped("state")
|
@@ -339,20 +272,20 @@ class SiiMixin(models.AbstractModel):
|
|
339
272
|
new_delay = (
|
340
273
|
record.sudo()
|
341
274
|
.with_context(company_id=company.id)
|
342
|
-
.with_delay(eta=eta if not record.
|
275
|
+
.with_delay(eta=eta if not record.aeat_send_failed else False)
|
343
276
|
.confirm_one_document()
|
344
277
|
)
|
345
278
|
job = queue_obj.search([("uuid", "=", new_delay.uuid)], limit=1)
|
346
279
|
setattr(record.sudo(), self._get_sii_jobs_field_name(), [(4, job.id)])
|
347
280
|
|
348
|
-
def
|
281
|
+
def _bind_service(self, client, port_name, address=None):
|
349
282
|
self.ensure_one()
|
350
283
|
service = client._get_service("siiService")
|
351
284
|
port = client._get_port(service, port_name)
|
352
285
|
address = address or port.binding_options["address"]
|
353
286
|
return client.create_service(port.binding.name, address)
|
354
287
|
|
355
|
-
def
|
288
|
+
def _connect_params_aeat(self, mapping_key):
|
356
289
|
self.ensure_one()
|
357
290
|
agency = self.company_id.tax_agency_id
|
358
291
|
if not agency:
|
@@ -363,19 +296,6 @@ class SiiMixin(models.AbstractModel):
|
|
363
296
|
agency = self.env.ref("l10n_es_aeat.aeat_tax_agency_spain")
|
364
297
|
return agency._connect_params_sii(mapping_key, self.company_id)
|
365
298
|
|
366
|
-
def _connect_sii(self, mapping_key):
|
367
|
-
self.ensure_one()
|
368
|
-
public_crt, private_key = self.env["l10n.es.aeat.certificate"].get_certificates(
|
369
|
-
company=self.company_id
|
370
|
-
)
|
371
|
-
params = self._connect_params_sii(mapping_key)
|
372
|
-
session = Session()
|
373
|
-
session.cert = (public_crt, private_key)
|
374
|
-
transport = Transport(session=session)
|
375
|
-
history = HistoryPlugin()
|
376
|
-
client = Client(wsdl=params["wsdl"], transport=transport, plugins=[history])
|
377
|
-
return self._bind_sii(client, params["port_name"], params["address"])
|
378
|
-
|
379
299
|
def _get_sii_gen_type(self):
|
380
300
|
"""Make a choice for general invoice type
|
381
301
|
|
@@ -394,39 +314,29 @@ class SiiMixin(models.AbstractModel):
|
|
394
314
|
res = 1
|
395
315
|
return res
|
396
316
|
|
397
|
-
def
|
317
|
+
def _is_aeat_simplified_invoice(self):
|
398
318
|
"""Inheritable method to allow control when an
|
399
319
|
invoice are simplified or normal"""
|
400
|
-
partner = self.
|
401
|
-
return partner.
|
320
|
+
partner = self._aeat_get_partner()
|
321
|
+
return partner.aeat_simplified_invoice
|
402
322
|
|
403
|
-
def
|
323
|
+
def _aeat_check_exceptions(self):
|
404
324
|
"""Inheritable method for exceptions control when sending SII invoices."""
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
(
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
_("
|
419
|
-
|
420
|
-
if not self.company_id.sii_enabled:
|
421
|
-
raise UserError(_("This company doesn't have SII enabled."))
|
422
|
-
if not self.sii_enabled:
|
423
|
-
raise UserError(_("This invoice is not SII enabled."))
|
424
|
-
|
425
|
-
def _get_mapping_key(self):
|
426
|
-
raise NotImplementedError()
|
427
|
-
|
428
|
-
def _get_document_date(self):
|
429
|
-
raise NotImplementedError()
|
325
|
+
res = super()._aeat_check_exceptions()
|
326
|
+
if self.company_id.sii_enabled:
|
327
|
+
gen_type = self._get_sii_gen_type()
|
328
|
+
partner = self._aeat_get_partner()
|
329
|
+
country_code = self._get_aeat_country_code()
|
330
|
+
is_simplified_invoice = self._is_aeat_simplified_invoice()
|
331
|
+
if (
|
332
|
+
(gen_type != 3 or country_code == "ES")
|
333
|
+
and not partner.vat
|
334
|
+
and not is_simplified_invoice
|
335
|
+
):
|
336
|
+
raise UserError(_("The partner has not a VAT configured."))
|
337
|
+
if not self.sii_enabled:
|
338
|
+
raise UserError(_("This invoice is not SII enabled."))
|
339
|
+
return res
|
430
340
|
|
431
341
|
def _get_document_fiscal_date(self):
|
432
342
|
raise NotImplementedError()
|
@@ -437,9 +347,6 @@ class SiiMixin(models.AbstractModel):
|
|
437
347
|
def _get_document_period(self):
|
438
348
|
return "%02d" % fields.Date.to_date(self._get_document_fiscal_date()).month
|
439
349
|
|
440
|
-
def _get_document_serial_number(self):
|
441
|
-
raise NotImplementedError()
|
442
|
-
|
443
350
|
def _get_document_product_exempt(self, applied_taxes):
|
444
351
|
raise NotImplementedError()
|
445
352
|
|
@@ -524,7 +431,7 @@ class SiiMixin(models.AbstractModel):
|
|
524
431
|
self.ensure_one()
|
525
432
|
if "DesgloseFactura" not in taxes_dict:
|
526
433
|
return False
|
527
|
-
country_code = self.
|
434
|
+
country_code = self._get_aeat_country_code()
|
528
435
|
sii_gen_type = self._get_sii_gen_type()
|
529
436
|
if "DesgloseTipoOperacion" in taxes_dict:
|
530
437
|
# DesgloseTipoOperacion and DesgloseFactura are Exclusive
|
@@ -537,7 +444,7 @@ class SiiMixin(models.AbstractModel):
|
|
537
444
|
# DesgloseTipoOperacion required for national operations
|
538
445
|
# with 'IDOtro' in the SII identifier block
|
539
446
|
return True
|
540
|
-
elif sii_gen_type == 1 and (self.
|
447
|
+
elif sii_gen_type == 1 and (self._aeat_get_partner().vat or "").startswith(
|
541
448
|
"ESN"
|
542
449
|
):
|
543
450
|
# DesgloseTipoOperacion required if customer's country is Spain and
|
@@ -553,19 +460,19 @@ class SiiMixin(models.AbstractModel):
|
|
553
460
|
self.ensure_one()
|
554
461
|
taxes_dict = {}
|
555
462
|
date = self._get_document_fiscal_date()
|
556
|
-
taxes_sfesb = self.
|
557
|
-
taxes_sfesbe = self.
|
558
|
-
taxes_sfesisp = self.
|
463
|
+
taxes_sfesb = self._get_aeat_taxes_map(["SFESB"], date)
|
464
|
+
taxes_sfesbe = self._get_aeat_taxes_map(["SFESBE"], date)
|
465
|
+
taxes_sfesisp = self._get_aeat_taxes_map(["SFESISP"], date)
|
559
466
|
# taxes_sfesisps = self._get_taxes_map(['SFESISPS'])
|
560
|
-
taxes_sfens = self.
|
561
|
-
taxes_sfess = self.
|
562
|
-
taxes_sfesse = self.
|
563
|
-
taxes_sfesns = self.
|
564
|
-
taxes_not_in_total = self.
|
565
|
-
taxes_not_in_total_neg = self.
|
467
|
+
taxes_sfens = self._get_aeat_taxes_map(["SFENS"], date)
|
468
|
+
taxes_sfess = self._get_aeat_taxes_map(["SFESS"], date)
|
469
|
+
taxes_sfesse = self._get_aeat_taxes_map(["SFESSE"], date)
|
470
|
+
taxes_sfesns = self._get_aeat_taxes_map(["SFESNS"], date)
|
471
|
+
taxes_not_in_total = self._get_aeat_taxes_map(["NotIncludedInTotal"], date)
|
472
|
+
taxes_not_in_total_neg = self._get_aeat_taxes_map(
|
566
473
|
["NotIncludedInTotalNegative"], date
|
567
474
|
)
|
568
|
-
base_not_in_total = self.
|
475
|
+
base_not_in_total = self._get_aeat_taxes_map(["BaseNotIncludedInTotal"], date)
|
569
476
|
not_in_amount_total = 0
|
570
477
|
exempt_cause = self._get_sii_exempt_cause(taxes_sfesbe + taxes_sfesse)
|
571
478
|
tax_lines = self._get_tax_info()
|
@@ -667,9 +574,6 @@ class SiiMixin(models.AbstractModel):
|
|
667
574
|
del taxes_dict["DesgloseFactura"]
|
668
575
|
return taxes_dict, not_in_amount_total
|
669
576
|
|
670
|
-
def _get_document_amount_total(self):
|
671
|
-
raise NotImplementedError()
|
672
|
-
|
673
577
|
def _get_sii_invoice_type(self):
|
674
578
|
raise NotImplementedError()
|
675
579
|
|
@@ -683,7 +587,7 @@ class SiiMixin(models.AbstractModel):
|
|
683
587
|
country_code,
|
684
588
|
identifier_type,
|
685
589
|
identifier,
|
686
|
-
) = self.
|
590
|
+
) = self._aeat_get_partner()._parse_aeat_vat_info()
|
687
591
|
# Limpiar alfanum
|
688
592
|
if identifier:
|
689
593
|
identifier = "".join(e for e in identifier if e.isalnum()).upper()
|
@@ -691,7 +595,7 @@ class SiiMixin(models.AbstractModel):
|
|
691
595
|
identifier = "NO_DISPONIBLE"
|
692
596
|
identifier_type = "06"
|
693
597
|
if gen_type == 1:
|
694
|
-
if "1117" in (self.
|
598
|
+
if "1117" in (self.aeat_send_error or ""):
|
695
599
|
return {
|
696
600
|
"IDOtro": {
|
697
601
|
"CodigoPais": country_code,
|
@@ -707,8 +611,8 @@ class SiiMixin(models.AbstractModel):
|
|
707
611
|
"CodigoPais": country_code,
|
708
612
|
"IDType": identifier_type,
|
709
613
|
"ID": country_code + identifier
|
710
|
-
if self.
|
711
|
-
in self.
|
614
|
+
if self._aeat_get_partner()._map_aeat_country_code(country_code)
|
615
|
+
in self._aeat_get_partner()._get_aeat_europe_codes()
|
712
616
|
else identifier,
|
713
617
|
},
|
714
618
|
}
|
@@ -730,7 +634,7 @@ class SiiMixin(models.AbstractModel):
|
|
730
634
|
elif gen_type == 3:
|
731
635
|
return {"NIF": identifier}
|
732
636
|
|
733
|
-
def
|
637
|
+
def _get_aeat_invoice_dict_out(self, cancel=False):
|
734
638
|
"""Build dict with data to send to AEAT WS for document types:
|
735
639
|
out_invoice and out_refund.
|
736
640
|
|
@@ -740,11 +644,11 @@ class SiiMixin(models.AbstractModel):
|
|
740
644
|
"""
|
741
645
|
self.ensure_one()
|
742
646
|
document_date = self._change_date_format(self._get_document_date())
|
743
|
-
partner = self.
|
647
|
+
partner = self._aeat_get_partner()
|
744
648
|
company = self.company_id
|
745
649
|
fiscal_year = self._get_document_fiscal_year()
|
746
650
|
period = self._get_document_period()
|
747
|
-
is_simplified_invoice = self.
|
651
|
+
is_simplified_invoice = self._is_aeat_simplified_invoice()
|
748
652
|
serial_number = self._get_document_serial_number()
|
749
653
|
inv_dict = {
|
750
654
|
"IDFactura": {
|
@@ -782,7 +686,7 @@ class SiiMixin(models.AbstractModel):
|
|
782
686
|
exp_dict["Contraparte"].update(self._get_sii_identifier())
|
783
687
|
return inv_dict
|
784
688
|
|
785
|
-
def
|
689
|
+
def _get_aeat_invoice_dict_in(self, cancel=False):
|
786
690
|
"""Build dict with data to send to AEAT WS for invoice types:
|
787
691
|
in_invoice and in_refund.
|
788
692
|
|
@@ -792,15 +696,15 @@ class SiiMixin(models.AbstractModel):
|
|
792
696
|
"""
|
793
697
|
raise NotImplementedError()
|
794
698
|
|
795
|
-
def
|
699
|
+
def _get_aeat_invoice_dict(self):
|
796
700
|
self.ensure_one()
|
797
|
-
self.
|
701
|
+
self._aeat_check_exceptions()
|
798
702
|
inv_dict = {}
|
799
703
|
mapping_key = self._get_mapping_key()
|
800
704
|
if mapping_key in ["out_invoice", "out_refund"]:
|
801
|
-
inv_dict = self.
|
705
|
+
inv_dict = self._get_aeat_invoice_dict_out()
|
802
706
|
elif mapping_key in ["in_invoice", "in_refund"]:
|
803
|
-
inv_dict = self.
|
707
|
+
inv_dict = self._get_aeat_invoice_dict_in()
|
804
708
|
round_by_keys(
|
805
709
|
inv_dict,
|
806
710
|
[
|
@@ -835,24 +739,24 @@ class SiiMixin(models.AbstractModel):
|
|
835
739
|
for document in self.filtered(
|
836
740
|
lambda i: i.state in self._get_valid_document_states()
|
837
741
|
):
|
838
|
-
if document.
|
742
|
+
if document.aeat_state == "not_sent":
|
839
743
|
tipo_comunicacion = "A0"
|
840
744
|
else:
|
841
745
|
tipo_comunicacion = "A1"
|
842
|
-
header = document.
|
746
|
+
header = document._get_aeat_header(tipo_comunicacion)
|
843
747
|
doc_vals = {
|
844
|
-
"
|
748
|
+
"aeat_header_sent": json.dumps(header, indent=4),
|
845
749
|
}
|
846
|
-
# add this extra try except in case
|
750
|
+
# add this extra try except in case _get_aeat_invoice_dict fails
|
847
751
|
# if not, get the value doc_dict for the next try and except below
|
848
752
|
try:
|
849
|
-
inv_dict = document.
|
753
|
+
inv_dict = document._get_aeat_invoice_dict()
|
850
754
|
except Exception as fault:
|
851
755
|
raise ValidationError(fault) from fault
|
852
756
|
try:
|
853
757
|
mapping_key = document._get_mapping_key()
|
854
|
-
serv = document.
|
855
|
-
doc_vals["
|
758
|
+
serv = document._connect_aeat(mapping_key)
|
759
|
+
doc_vals["aeat_content_sent"] = json.dumps(inv_dict, indent=4)
|
856
760
|
if mapping_key in ["out_invoice", "out_refund"]:
|
857
761
|
res = serv.SuministroLRFacturasEmitidas(header, inv_dict)
|
858
762
|
elif mapping_key in ["in_invoice", "in_refund"]:
|
@@ -866,9 +770,9 @@ class SiiMixin(models.AbstractModel):
|
|
866
770
|
if res["EstadoEnvio"] == "Correcto":
|
867
771
|
doc_vals.update(
|
868
772
|
{
|
869
|
-
"
|
773
|
+
"aeat_state": "sent",
|
870
774
|
"sii_csv": res["CSV"],
|
871
|
-
"
|
775
|
+
"aeat_send_failed": False,
|
872
776
|
}
|
873
777
|
)
|
874
778
|
elif (
|
@@ -877,15 +781,15 @@ class SiiMixin(models.AbstractModel):
|
|
877
781
|
):
|
878
782
|
doc_vals.update(
|
879
783
|
{
|
880
|
-
"
|
784
|
+
"aeat_state": "sent_w_errors",
|
881
785
|
"sii_csv": res["CSV"],
|
882
|
-
"
|
786
|
+
"aeat_send_failed": True,
|
883
787
|
}
|
884
788
|
)
|
885
789
|
else:
|
886
|
-
doc_vals["
|
790
|
+
doc_vals["aeat_send_failed"] = True
|
887
791
|
if (
|
888
|
-
"
|
792
|
+
"aeat_state" in doc_vals
|
889
793
|
and not document.sii_account_registration_date
|
890
794
|
and mapping_key[:2] == "in"
|
891
795
|
):
|
@@ -899,7 +803,7 @@ class SiiMixin(models.AbstractModel):
|
|
899
803
|
str(res_line["CodigoErrorRegistro"]),
|
900
804
|
str(res_line["DescripcionErrorRegistro"])[:60],
|
901
805
|
)
|
902
|
-
doc_vals["
|
806
|
+
doc_vals["aeat_send_error"] = send_error
|
903
807
|
document.write(doc_vals)
|
904
808
|
except Exception as fault:
|
905
809
|
new_cr = Registry(self.env.cr.dbname).cursor()
|
@@ -907,10 +811,10 @@ class SiiMixin(models.AbstractModel):
|
|
907
811
|
document = env[document._name].browse(document.id)
|
908
812
|
doc_vals.update(
|
909
813
|
{
|
910
|
-
"
|
911
|
-
"
|
814
|
+
"aeat_send_failed": True,
|
815
|
+
"aeat_send_error": repr(fault)[:60],
|
912
816
|
"sii_return": repr(fault),
|
913
|
-
"
|
817
|
+
"aeat_content_sent": json.dumps(inv_dict, indent=4),
|
914
818
|
}
|
915
819
|
)
|
916
820
|
document.write(doc_vals)
|
@@ -367,7 +367,7 @@ ul.auto-toc {
|
|
367
367
|
!! This file is generated by oca-gen-addon-readme !!
|
368
368
|
!! changes will be overwritten. !!
|
369
369
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
370
|
-
!! source digest: sha256:
|
370
|
+
!! source digest: sha256:10113ecb0d04bfe1b6ece65a39892141bff2e511f72909f4cac9e9bb7d86d0ed
|
371
371
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
372
372
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Mature" src="https://img.shields.io/badge/maturity-Mature-brightgreen.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/l10n-spain/tree/16.0/l10n_es_aeat_sii_oca"><img alt="OCA/l10n-spain" src="https://img.shields.io/badge/github-OCA%2Fl10n--spain-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/l10n-spain-16-0/l10n-spain-16-0-l10n_es_aeat_sii_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/l10n-spain&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
373
373
|
<p>Módulo para la presentación inmediata del IVA
|
@@ -108,7 +108,7 @@ class TestL10nEsAeatSiiBase(TestL10nEsAeatModBase, TestL10nEsAeatCertificateBase
|
|
108
108
|
if extra_vals:
|
109
109
|
vals.update(extra_vals)
|
110
110
|
invoice = self.env["account.move"].create(vals)
|
111
|
-
result_dict = invoice.
|
111
|
+
result_dict = invoice._get_aeat_invoice_dict()
|
112
112
|
path = get_resource_path(module, "tests/json", json_file)
|
113
113
|
if not path:
|
114
114
|
raise Exception("Incorrect JSON file: %s" % json_file)
|
@@ -211,7 +211,7 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
211
211
|
{"partner_id": eu_customer.id, "fiscal_position_id": fp_extra.id}
|
212
212
|
)
|
213
213
|
invoice.action_post()
|
214
|
-
sii_info = invoice.
|
214
|
+
sii_info = invoice._get_aeat_invoice_dict()
|
215
215
|
self.assertEqual(
|
216
216
|
sii_info["FacturaExpedida"]["Contraparte"],
|
217
217
|
{
|
@@ -367,7 +367,7 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
367
367
|
company = invoice.company_id
|
368
368
|
tax_agency = company.tax_agency_id
|
369
369
|
self.sii_cert.company_id.tax_agency_id = tax_agency
|
370
|
-
proxy = invoice.
|
370
|
+
proxy = invoice._connect_aeat(invoice.move_type)
|
371
371
|
address = proxy._binding_options["address"]
|
372
372
|
self.assertTrue(address)
|
373
373
|
if company.sii_test and tax_agency:
|
@@ -421,13 +421,13 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
421
421
|
invoice.move_type = "out_refund"
|
422
422
|
self.assertEqual(invoice.sii_refund_type, "I")
|
423
423
|
|
424
|
-
def
|
425
|
-
self.assertFalse(self.invoice.
|
426
|
-
self.partner.
|
427
|
-
self.assertTrue(self.invoice.
|
424
|
+
def test_is_aeat_simplified_invoice(self):
|
425
|
+
self.assertFalse(self.invoice._is_aeat_simplified_invoice())
|
426
|
+
self.partner.aeat_simplified_invoice = True
|
427
|
+
self.assertTrue(self.invoice._is_aeat_simplified_invoice())
|
428
428
|
|
429
|
-
def
|
430
|
-
self.partner.
|
429
|
+
def test_aeat_check_exceptions_case_supplier_simplified(self):
|
430
|
+
self.partner.aeat_simplified_invoice = True
|
431
431
|
invoice = self.env["account.move"].create(
|
432
432
|
{
|
433
433
|
"partner_id": self.partner.id,
|
@@ -436,9 +436,9 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
436
436
|
}
|
437
437
|
)
|
438
438
|
with self.assertRaises(exceptions.UserError):
|
439
|
-
invoice.
|
439
|
+
invoice._aeat_check_exceptions()
|
440
440
|
|
441
|
-
def
|
441
|
+
def test_aeat_check_exceptions_case_supplier_on_post(self):
|
442
442
|
"""Check sii exceptions when posting supplier bills"""
|
443
443
|
supplier = self.supplier.copy()
|
444
444
|
supplier.country_id = self.env.ref("base.es")
|
@@ -473,14 +473,14 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
473
473
|
self.assertFalse(draft_invoice.exists())
|
474
474
|
|
475
475
|
def test_unlink_invoice_when_sent_to_sii(self):
|
476
|
-
self.invoice.
|
476
|
+
self.invoice.aeat_state = "sent"
|
477
477
|
self.invoice.button_draft() # Convert to draft to check only SII exception
|
478
478
|
with self.assertRaises(exceptions.UserError):
|
479
479
|
self.invoice.unlink()
|
480
480
|
|
481
481
|
def test_account_move_sii_write_exceptions(self):
|
482
482
|
# out_invoice
|
483
|
-
self.invoice.
|
483
|
+
self.invoice.aeat_state = "sent"
|
484
484
|
with self.assertRaises(exceptions.UserError):
|
485
485
|
self.invoice.write({"invoice_date": "2022-01-01"})
|
486
486
|
with self.assertRaises(exceptions.UserError):
|
@@ -488,7 +488,7 @@ class TestL10nEsAeatSii(TestL10nEsAeatSiiBase):
|
|
488
488
|
# in_invoice
|
489
489
|
in_invoice = self._create_invoice_for_sii("in_invoice")
|
490
490
|
in_invoice.ref = "REF"
|
491
|
-
in_invoice.
|
491
|
+
in_invoice.aeat_state = "sent"
|
492
492
|
partner = self.partner.copy()
|
493
493
|
with self.assertRaises(exceptions.UserError):
|
494
494
|
in_invoice.write({"partner_id": partner.id})
|
@@ -16,26 +16,26 @@
|
|
16
16
|
name="group_sii"
|
17
17
|
attrs="{'invisible': [('sii_enabled', '=', False)]}"
|
18
18
|
>
|
19
|
-
<field name="
|
19
|
+
<field name="aeat_active" />
|
20
20
|
<field
|
21
21
|
name="sii_registration_key_sale"
|
22
|
-
attrs="{'invisible': [('
|
22
|
+
attrs="{'invisible': [('aeat_active', '!=', True)]}"
|
23
23
|
/>
|
24
24
|
<field
|
25
25
|
name="sii_registration_key_purchase"
|
26
|
-
attrs="{'invisible': [('
|
26
|
+
attrs="{'invisible': [('aeat_active', '!=', True)]}"
|
27
27
|
/>
|
28
28
|
<field
|
29
29
|
name="sii_no_taxable_cause"
|
30
|
-
attrs="{'invisible': [('
|
30
|
+
attrs="{'invisible': [('aeat_active', '!=', True)]}"
|
31
31
|
/>
|
32
32
|
<field
|
33
33
|
name="sii_exempt_cause"
|
34
|
-
attrs="{'invisible': [('
|
34
|
+
attrs="{'invisible': [('aeat_active', '!=', True)]}"
|
35
35
|
/>
|
36
36
|
<field
|
37
37
|
name="sii_partner_identification_type"
|
38
|
-
attrs="{'invisible': [('
|
38
|
+
attrs="{'invisible': [('aeat_active', '!=', True)]}"
|
39
39
|
/>
|
40
40
|
</group>
|
41
41
|
</xpath>
|