Skip to content

Commit

Permalink
[FIX] l10n_es_aeat_sii: float_round error in BaseImponible
Browse files Browse the repository at this point in the history
Extension of 8a65078

En las lineas https://github.com/OCA/l10n-spain/blob/fabc3a39eda6c595045906a3a612a18a6d902f9f/l10n_es_aeat_sii/models/account_invoice.py#L491-L492
y
https://github.com/OCA/l10n-spain/blob/fabc3a39eda6c595045906a3a612a18a6d902f9f/l10n_es_aeat_sii/models/account_invoice.py#L537-L538

la suma (+=) genera un error de coma flotante, para reproducir el error desde la consola Python:

Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
>>> 170.61 + 107.35
277.96000000000004

Example on customer exempt invoices:

- "Invoice Lines" tab:
    * [
        { quantity: 1,  price_unit: 56.31,  invoice_line_tax_ids: ['IVA Exento Repercutido'] },
        { quantity: 58, price_unit: 0.88,   invoice_line_tax_ids: ['IVA Exento Repercutido'] },
        { quantity: 1,  price_unit: 170.61, invoice_line_tax_ids: ['IVA Exento Repercutido'] },
      ]

- "SII" tab:
    - "General" tab:

        * Error de envio SII: 1100 | Valor o tipo incorrecto del campo: BaseImponible

    - "Technical" tab:

        * Último contenido enviado al SII
        {
            "FacturaExpedida": {
                "ClaveRegimenEspecialOTrascendencia": "01",
                "ImporteTotal": 277.96,
                "DescripcionOperacion": "/",
                "TipoDesglose": {
                    "DesgloseTipoOperacion": {
                        "PrestacionServicios": {
                            "Sujeta": {
                                "Exenta": {
                                    "DetalleExenta": [
                                        {
                                            "BaseImponible": 277.96000000000004,
                                            "CausaExencion": "E1"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                },
                "TipoFactura": "F1",
                "Contraparte": {
                    "NIF": "XXXXXXXX",
                    "NombreRazon": "XXXXXXXXXXXXXXXXXXXXXXXX"
                }
            },
            "IDFactura": {
                "NumSerieFacturaEmisor": "22902",
                "FechaExpedicionFacturaEmisor": "17-01-2019",
                "IDEmisorFactura": {
                    "NIF": "XXXXXXXXXX"
                }
            },
            "PeriodoLiquidacion": {
                "Periodo": "01",
                "Ejercicio": 2019
            }
        }

        * Retorno SII

        {
            'CSV': None,
            'DatosPresentacion': None,
            'Cabecera': {
                'IDVersionSii': '1.1',
                'Titular': {
                    'NombreRazon': 'XXXXXXXXXXXX',
                    'NIFRepresentante': None,
                    'NIF': 'XXXXXXXXXXXXX'
                },
                'TipoComunicacion': 'A0'
            },
            'EstadoEnvio': 'Incorrecto',
            'RespuestaLinea': [
                {
                    'IDFactura': {
                        'IDEmisorFactura': {
                            'NIF': 'XXXXXXXXXXXX'
                        },
                        'NumSerieFacturaEmisor': '22902',
                        'NumSerieFacturaEmisorResumenFin': None,
                        'FechaExpedicionFacturaEmisor': '17-01-2019'
                    },
                    'RefExterna': None,
                    'EstadoRegistro': 'Incorrecto',
                    'CodigoErrorRegistro': 1100,
                    'DescripcionErrorRegistro': 'Valor o tipo incorrecto del campo: BaseImponible',
                    'CSV': None,
                    'RegistroDuplicado': None
                }
            ]
        }
  • Loading branch information
eantones authored and OCA-git-bot committed Aug 7, 2019
1 parent a11b769 commit aa4761e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 21 deletions.
58 changes: 37 additions & 21 deletions l10n_es_aeat_sii/models/account_invoice.py
Expand Up @@ -52,6 +52,22 @@ def empty_decorator_factory(*argv, **kwargs):
SII_MACRODATA_LIMIT = 100000000.0


def round_by_keys(elem, search_keys, prec=2):
""" This uses ``round`` method directly as if has been tested that Odoo's
``float_round`` still returns incorrect amounts for certain values. Try
3 units x 3,77 €/unit with 10% tax and you will be hit by the error
(on regular x86 architectures)."""
if isinstance(elem, dict):
for key, value in elem.items():
if key in search_keys:
elem[key] = round(elem[key], prec)
else:
round_by_keys(value, search_keys)
elif isinstance(elem, list):
for value in elem:
round_by_keys(value, search_keys)


class AccountInvoice(models.Model):
_inherit = 'account.invoice'

Expand Down Expand Up @@ -387,11 +403,6 @@ def _get_sii_tax_line_req(self, tax):
def _get_sii_tax_dict(self, tax_line, sign):
"""Get the SII tax dictionary for the passed tax line.
This uses ``round`` method directly as if has been tested that Odoo's
``float_round`` still returns incorrect amounts for certain values. Try
3 units x 3,77 €/unit with 10% tax and you will be hit by the error
(on regular x86 architectures).
:param self: Single invoice record.
:param tax_line: Tax line that is being analyzed.
:param sign: Sign of the operation (only refund by differences is
Expand All @@ -405,21 +416,21 @@ def _get_sii_tax_dict(self, tax_line, sign):
tax_type = abs(tax.amount)
tax_dict = {
'TipoImpositivo': str(tax_type),
'BaseImponible': sign * abs(round(tax_line.base_company, 2)),
'BaseImponible': sign * abs(tax_line.base_company),
}
if self.type in ['out_invoice', 'out_refund']:
key = 'CuotaRepercutida'
else:
key = 'CuotaSoportada'
tax_dict[key] = sign * abs(round(tax_line.amount_company, 2))
tax_dict[key] = sign * abs(tax_line.amount_company)
# Recargo de equivalencia
re_tax_line = self._get_sii_tax_line_req(tax)
if re_tax_line:
tax_dict['TipoRecargoEquivalencia'] = (
abs(re_tax_line.tax_id.amount)
)
tax_dict['CuotaRecargoEquivalencia'] = (
sign * abs(round(re_tax_line.amount_company, 2))
sign * abs(re_tax_line.amount_company)
)
return tax_dict

Expand Down Expand Up @@ -490,7 +501,7 @@ def _get_sii_out_taxes(self):
if exempt_cause:
det_dict['CausaExencion'] = exempt_cause
det_dict['BaseImponible'] += (
round(tax_line.base_company, 2) * sign)
tax_line.base_company * sign)
else:
sub_dict.setdefault('NoExenta', {
'TipoNoExenta': (
Expand All @@ -516,7 +527,7 @@ def _get_sii_out_taxes(self):
'NoSujeta', {default_no_taxable_cause: 0},
)
nsub_dict[default_no_taxable_cause] += (
round(tax_line.base_company, 2) * sign)
tax_line.base_company * sign)
if tax in (taxes_sfess + taxes_sfesse + taxes_sfesns):
type_breakdown = taxes_dict.setdefault(
'DesgloseTipoOperacion', {
Expand All @@ -536,7 +547,7 @@ def _get_sii_out_taxes(self):
if exempt_cause:
det_dict['CausaExencion'] = exempt_cause
det_dict['BaseImponible'] += (
round(tax_line.base_company, 2) * sign)
tax_line.base_company * sign)
if tax in taxes_sfess:
# TODO l10n_es_ no tiene impuesto ISP de servicios
# if tax in taxes_sfesisps:
Expand Down Expand Up @@ -568,6 +579,13 @@ def _get_sii_out_taxes(self):
taxes_dict['DesgloseTipoOperacion']['Entrega'] = \
taxes_dict['DesgloseFactura']
del taxes_dict['DesgloseFactura']
round_by_keys(taxes_dict, [
'BaseImponible', 'CuotaRepercutida', 'CuotaSoportada',
'TipoRecargoEquivalencia', 'CuotaRecargoEquivalencia',
'ImportePorArticulos7_14_Otros', 'ImporteTAIReglasLocalizacion',
'ImporteTotal', 'BaseRectificada', 'CuotaRectificada',
'CuotaDeducible'
])
return taxes_dict

@api.multi
Expand Down Expand Up @@ -609,7 +627,7 @@ def _get_sii_in_taxes(self):
'DesgloseIVA', {'DetalleIVA': []},
)
sfrns_dict['DetalleIVA'].append({
'BaseImponible': sign * round(tax_line.base_company, 2),
'BaseImponible': sign * tax_line.base_company,
})
elif tax in taxes_sfrsa:
sfrsa_dict = taxes_dict.setdefault(
Expand Down Expand Up @@ -728,7 +746,7 @@ def _get_sii_invoice_dict_out(self, cancel=False):
"DescripcionOperacion": self.sii_description,
"TipoDesglose": self._get_sii_out_taxes(),
"ImporteTotal": abs(
round(self.amount_total_company_signed, 2)
self.amount_total_company_signed
) * sign,
}
if self.sii_macrodata:
Expand Down Expand Up @@ -762,14 +780,12 @@ def _get_sii_invoice_dict_out(self, cancel=False):
if self.sii_refund_type == 'S':
origin = self.refund_invoice_id
exp_dict['ImporteRectificacion'] = {
'BaseRectificada': round(
abs(origin.amount_untaxed_signed), 2,
'BaseRectificada': abs(
origin.amount_untaxed_signed
),
'CuotaRectificada': round(
abs(
origin.amount_total_company_signed -
origin.amount_untaxed_signed
), 2,
'CuotaRectificada': abs(
origin.amount_total_company_signed -
origin.amount_untaxed_signed
),
}
return inv_dict
Expand Down Expand Up @@ -831,7 +847,7 @@ def _get_sii_invoice_dict_in(self, cancel=False):
},
"FechaRegContable": reg_date,
"ImporteTotal": abs(self.amount_total_company_signed) * sign,
"CuotaDeducible": round(tax_amount * sign, 2),
"CuotaDeducible": tax_amount * sign,
}
if self.sii_macrodata:
inv_dict["FacturaRecibida"].update(Macrodato="S")
Expand Down
1 change: 1 addition & 0 deletions l10n_es_aeat_sii/readme/CONTRIBUTORS.rst
Expand Up @@ -10,3 +10,4 @@
* Javi Melendez <javimelex@gmail.com>
* Santi Argüeso - Comunitea S.L. <santi@comunitea.com>
* Angel Moya - PESOL <angel.moya@pesol.es>
* Eric Antonés - NuoBiT Solutions, S.L. <eantones@nuobit.com>

0 comments on commit aa4761e

Please sign in to comment.