diff --git a/l10n_es_aeat_sii/README.rst b/l10n_es_aeat_sii/README.rst index d106f65f019..e05a346e9ac 100644 --- a/l10n_es_aeat_sii/README.rst +++ b/l10n_es_aeat_sii/README.rst @@ -142,7 +142,7 @@ Contributors * Jordi Tolsà * Ismael Calvo * Omar Castiñeira - Comunitea S.L. -* Juanjo Algaz +* Juanjo Algaz , Planeta Huerto * Pedro M. Baeza * Javi Melendez * Santi Argüeso - Comunitea S.L. diff --git a/l10n_es_aeat_sii/data/aeat_sii_map_data.xml b/l10n_es_aeat_sii/data/aeat_sii_map_data.xml index 1ea37592b83..74466a4bf2e 100644 --- a/l10n_es_aeat_sii/data/aeat_sii_map_data.xml +++ b/l10n_es_aeat_sii/data/aeat_sii_map_data.xml @@ -174,4 +174,41 @@ SuministroFactRecibidas No Deducible + + NotIncludedInTotal + + + Impuestos no incluidos en ImporteTotal + + diff --git a/l10n_es_aeat_sii/models/account_invoice.py b/l10n_es_aeat_sii/models/account_invoice.py index 3225ab6bd71..9d0e5d8299d 100644 --- a/l10n_es_aeat_sii/models/account_invoice.py +++ b/l10n_es_aeat_sii/models/account_invoice.py @@ -485,14 +485,18 @@ def _get_sii_out_taxes(self): taxes_sfess = self._get_sii_taxes_map(['SFESS']) taxes_sfesse = self._get_sii_taxes_map(['SFESSE']) taxes_sfesns = self._get_sii_taxes_map(['SFESNS']) + taxes_not_in_total = self._get_sii_taxes_map(['NotIncludedInTotal']) # Check if refund type is 'By differences'. Negative amounts! sign = self._get_sii_sign() + not_in_amount_total = 0 exempt_cause = self._get_sii_exempt_cause(taxes_sfesbe + taxes_sfesse) for tax_line in self.tax_line_ids: tax = tax_line.tax_id breakdown_taxes = ( taxes_sfesb + taxes_sfesisp + taxes_sfens + taxes_sfesbe ) + if tax in taxes_not_in_total: + not_in_amount_total += tax_line.amount_total if tax in breakdown_taxes: tax_breakdown = taxes_dict.setdefault( 'DesgloseFactura', {}, @@ -589,7 +593,7 @@ def _get_sii_out_taxes(self): taxes_dict['DesgloseTipoOperacion']['Entrega'] = \ taxes_dict['DesgloseFactura'] del taxes_dict['DesgloseFactura'] - return taxes_dict + return taxes_dict, not_in_amount_total @api.model def _merge_tax_dict(self, vat_list, tax_dict, comp_key, merge_keys): @@ -614,11 +618,15 @@ def _get_sii_in_taxes(self): taxes_sfrisp = self._get_sii_taxes_map(['SFRISP']) taxes_sfrns = self._get_sii_taxes_map(['SFRNS']) taxes_sfrnd = self._get_sii_taxes_map(['SFRND']) + taxes_not_in_total = self._get_sii_taxes_map(['NotIncludedInTotal']) tax_amount = 0.0 + not_in_amount_total = 0.0 # Check if refund type is 'By differences'. Negative amounts! sign = self._get_sii_sign() for tax_line in self.tax_line_ids: tax = tax_line.tax_id + if tax in taxes_not_in_total: + not_in_amount_total += tax_line.amount_total if tax in taxes_sfrisp: base_dict = taxes_dict.setdefault( 'InversionSujetoPasivo', {'DetalleIVA': []}, @@ -650,7 +658,15 @@ def _get_sii_in_taxes(self): ["BaseImponible", "CuotaSoportada"] ): base_dict['DetalleIVA'].append(tax_dict) - return taxes_dict, tax_amount + return taxes_dict, tax_amount, not_in_amount_total + + @api.multi + def _is_sii_simplified_invoice(self): + """Inheritable method to allow control when an + invoice are simplified or normal""" + partner = self.partner_id.commercial_partner_id + is_simplified = partner.sii_simplified_invoice + return is_simplified @api.multi def _sii_check_exceptions(self): @@ -660,12 +676,14 @@ def _sii_check_exceptions(self): gen_type = self._get_sii_gen_type() partner = self.partner_id.commercial_partner_id country_code = self._get_sii_country_code() - if partner.sii_simplified_invoice and self.type[:2] == 'in': + is_simplified_invoice = self._is_sii_simplified_invoice() + + if is_simplified_invoice and self.type[:2] == 'in': raise exceptions.Warning( _("You can't make a supplier simplified invoice.") ) if ((gen_type != 3 or country_code == 'ES') and - not partner.vat and not partner.sii_simplified_invoice): + not partner.vat and not is_simplified_invoice): raise exceptions.Warning( _("The partner has not a VAT configured.") ) @@ -713,6 +731,7 @@ def _get_sii_invoice_dict_out(self, cancel=False): company = self.company_id ejercicio = fields.Date.from_string(self.date).year periodo = '%02d' % fields.Date.from_string(self.date).month + is_simplified_invoice = self._is_sii_simplified_invoice() inv_dict = { "IDFactura": { "IDEmisorFactura": { @@ -732,24 +751,25 @@ def _get_sii_invoice_dict_out(self, cancel=False): if not cancel: # Check if refund type is 'By differences'. Negative amounts! sign = self._get_sii_sign() - simplied = partner.sii_simplified_invoice + tipo_desglose, not_in_amount_total = self._get_sii_out_taxes() + amount_total = ( + abs(self.amount_total_company_signed) - not_in_amount_total + ) * sign if self.type == 'out_refund': if self.sii_refund_specific_invoice_type: tipo_factura = self.sii_refund_specific_invoice_type else: - tipo_factura = 'R5' if simplied else 'R1' + tipo_factura = 'R5' if is_simplified_invoice else 'R1' else: - tipo_factura = 'F2' if simplied else 'F1' + tipo_factura = 'F2' if is_simplified_invoice else 'F1' inv_dict["FacturaExpedida"] = { "TipoFactura": tipo_factura, "ClaveRegimenEspecialOTrascendencia": ( self.sii_registration_key.code ), "DescripcionOperacion": self.sii_description, - "TipoDesglose": self._get_sii_out_taxes(), - "ImporteTotal": abs( - self.amount_total_company_signed - ) * sign, + "TipoDesglose": tipo_desglose, + "ImporteTotal": amount_total, } if self.sii_macrodata: inv_dict["FacturaExpedida"].update(Macrodato="S") @@ -770,7 +790,7 @@ def _get_sii_invoice_dict_out(self, cancel=False): } } exp_dict = inv_dict['FacturaExpedida'] - if not partner.sii_simplified_invoice: + if not is_simplified_invoice: # Simplified invoices don't have counterpart exp_dict["Contraparte"] = { "NombreRazon": partner.name[0:120], @@ -807,7 +827,8 @@ def _get_sii_invoice_dict_in(self, cancel=False): self._get_account_registration_date()) ejercicio = fields.Date.from_string(self.date).year periodo = '%02d' % fields.Date.from_string(self.date).month - desglose_factura, tax_amount = self._get_sii_in_taxes() + desglose_factura, tax_amount, not_in_amount_total = ( + self._get_sii_in_taxes()) inv_dict = { "IDFactura": { "IDEmisorFactura": {}, @@ -832,6 +853,9 @@ def _get_sii_invoice_dict_in(self, cancel=False): else: # Check if refund type is 'By differences'. Negative amounts! sign = self._get_sii_sign() + amount_total = ( + abs(self.amount_total_company_signed) - not_in_amount_total + ) * sign inv_dict["FacturaRecibida"] = { # TODO: Incluir los 5 tipos de facturas rectificativas "TipoFactura": ( @@ -848,7 +872,7 @@ def _get_sii_invoice_dict_in(self, cancel=False): ) }, "FechaRegContable": reg_date, - "ImporteTotal": abs(self.amount_total_company_signed) * sign, + "ImporteTotal": amount_total, "CuotaDeducible": tax_amount * sign, } if self.sii_macrodata: diff --git a/l10n_es_aeat_sii/readme/CONTRIBUTORS.rst b/l10n_es_aeat_sii/readme/CONTRIBUTORS.rst index a078e26d65c..fb5e9824384 100644 --- a/l10n_es_aeat_sii/readme/CONTRIBUTORS.rst +++ b/l10n_es_aeat_sii/readme/CONTRIBUTORS.rst @@ -5,7 +5,7 @@ * Jordi Tolsà * Ismael Calvo * Omar Castiñeira - Comunitea S.L. -* Juanjo Algaz +* Juanjo Algaz , Planeta Huerto * Pedro M. Baeza * Javi Melendez * Santi Argüeso - Comunitea S.L. diff --git a/l10n_es_aeat_sii/static/description/index.html b/l10n_es_aeat_sii/static/description/index.html index e90d2b1c466..bbd3cc4ecec 100644 --- a/l10n_es_aeat_sii/static/description/index.html +++ b/l10n_es_aeat_sii/static/description/index.html @@ -490,7 +490,7 @@

Contributors

  • Jordi Tolsà <jordi@studio73.es>
  • Ismael Calvo <ismael.calvo@factorlibre.es>
  • Omar Castiñeira - Comunitea S.L. <omar@comunitea.com>
  • -
  • Juanjo Algaz <jalgaz@gmail.com>
  • +
  • Juanjo Algaz <jalgaz@gmail.com>, Planeta Huerto <juanjoalgaz@planetahuerto.es>
  • Pedro M. Baeza <pedro.baeza@tecnativa.com>
  • Javi Melendez <javimelex@gmail.com>
  • Santi Argüeso - Comunitea S.L. <santi@comunitea.com>
  • diff --git a/l10n_es_aeat_sii/tests/test_l10n_es_aeat_sii.py b/l10n_es_aeat_sii/tests/test_l10n_es_aeat_sii.py index fc6d403d2e7..a22d0688866 100644 --- a/l10n_es_aeat_sii/tests/test_l10n_es_aeat_sii.py +++ b/l10n_es_aeat_sii/tests/test_l10n_es_aeat_sii.py @@ -11,7 +11,7 @@ try: from zeep.client import ServiceProxy -except (ImportError, IOError) as err: +except (ImportError, IOError): ServiceProxy = object CERTIFICATE_PATH = get_resource_path( @@ -39,6 +39,26 @@ def _deep_sort(obj): class TestL10nEsAeatSiiBase(common.SavepointCase): + + @classmethod + def _get_or_create_tax(cls, xml_id, name, tax_type, percentage, account): + tax = cls.env.ref("l10n_es." + xml_id, raise_if_not_found=False) + if not tax: + tax = cls.env['account.tax'].create({ + 'name': name, + 'type_tax_use': tax_type, + 'amount_type': 'percent', + 'amount': percentage, + 'account_id': account.id, + }) + cls.env['ir.model.data'].create({ + 'module': 'l10n_es', + 'name': xml_id, + 'model': tax._name, + 'res_id': tax.id, + }) + return tax + @classmethod def setUpClass(cls): super(TestL10nEsAeatSiiBase, cls).setUpClass() @@ -66,15 +86,13 @@ def setUpClass(cls): 'code': 'TAX', 'user_type_id': cls.account_type.id, }) - cls.tax = cls.env['account.tax'].create({ - 'name': 'Test tax 10%', - # Needed for discriminatory tax amount in supplier invoices - 'description': 'P_IVA10_BC', - 'type_tax_use': 'purchase', - 'amount_type': 'percent', - 'amount': '10', - 'account_id': cls.account_tax.id, - }) + cls.company = cls.env.user.company_id + xml_id = '%s_account_tax_template_p_iva10_bc' % cls.company.id + cls.tax = cls._get_or_create_tax( + xml_id, "Test tax 10%", "purchase", 10, cls.account_tax) + irpf_xml_id = '%s_account_tax_template_p_irpf19' % cls.company.id + cls.tax_irpf19 = cls._get_or_create_tax( + irpf_xml_id, "IRPF 19%", "purchase", -19, cls.account_tax) cls.env.user.company_id.sii_description_method = 'manual' cls.invoice = cls.env['account.invoice'].create({ 'partner_id': cls.partner.id, @@ -349,3 +367,49 @@ def test_refund_sii_refund_type_write(self): self.assertFalse(invoice.sii_refund_type) invoice.type = 'out_refund' self.assertEqual(invoice.sii_refund_type, 'I') + + def test_is_sii_simplified_invoice(self): + self.assertFalse(self.invoice._is_sii_simplified_invoice()) + self.partner.sii_simplified_invoice = True + self.assertTrue(self.invoice._is_sii_simplified_invoice()) + + def test_sii_check_exceptions_case_supplier_simplified(self): + self.partner.is_simplified_invoice = True + invoice = self.env['account.invoice'].create({ + 'partner_id': self.partner.id, + 'date_invoice': '2018-02-01', + 'type': 'in_invoice', + 'account_id': self.partner.property_account_payable_id.id, + }) + with self.assertRaises(exceptions.Warning): + invoice._sii_check_exceptions() + + def test_importe_total_when_supplier_invoice_with_irpf(self): + invoice = self.env['account.invoice'].create({ + 'partner_id': self.partner.id, + 'date_invoice': '2018-02-01', + 'date': '2018-02-01', + 'type': 'in_invoice', + 'reference': 'PH-2020-0031', + 'account_id': self.partner.property_account_payable_id.id, + 'invoice_line_ids': [ + (0, 0, { + 'product_id': self.product.id, + 'account_id': self.account_expense.id, + 'account_analytic_id': self.analytic_account.id, + 'name': 'Test line with irpf and iva', + 'price_unit': 100, + 'quantity': 1, + 'invoice_line_tax_ids': [ + (6, 0, self.tax.ids + self.tax_irpf19.ids)], + })], + 'sii_manual_description': '/', + }) + invoices = invoice._get_sii_invoice_dict() + importe_total = invoices['FacturaRecibida']['ImporteTotal'] + self.assertEqual(110, importe_total) + + def test_unlink_invoice_when_sent_to_sii(self): + self.invoice.sii_state = 'sent' + with self.assertRaises(exceptions.Warning): + self.invoice.unlink()