From 6438d2a493fe52f7aa855ba507594ec6e2d22b6a Mon Sep 17 00:00:00 2001 From: Samuel Villegas Date: Fri, 12 Sep 2025 12:49:49 -0300 Subject: [PATCH] feat(cte): Add parser for "Datos del Contribuyente" - Implemented `parse_taxpayer_data` to parse taxpayer data from CTE HTML. - Added `TaxpayerData` and `LastFiledDocument` data models. - Created tests to validate parser functionality with sample HTML input. Ref: https://app.shortcut.com/cordada/story/16534/ --- src/cl_sii/cte/data_models.py | 48 + src/cl_sii/cte/parsers.py | 89 +- src/tests/test_cte_parsers.py | 56 + .../test_data/sii-cte/cte_empty_f29.html | 1141 +++++++++++++++++ .../test_data/sii-cte/cte_empty_table.html | 511 ++++++++ 5 files changed, 1844 insertions(+), 1 deletion(-) create mode 100644 src/tests/test_data/sii-cte/cte_empty_f29.html create mode 100644 src/tests/test_data/sii-cte/cte_empty_table.html diff --git a/src/cl_sii/cte/data_models.py b/src/cl_sii/cte/data_models.py index 91155774..c38b1809 100644 --- a/src/cl_sii/cte/data_models.py +++ b/src/cl_sii/cte/data_models.py @@ -1,6 +1,8 @@ from __future__ import annotations from collections.abc import Sequence +from datetime import date +from typing import Optional import pydantic @@ -38,3 +40,49 @@ class LegalRepresentative: """ Fecha de incorporación. """ + + +@pydantic.dataclasses.dataclass( + frozen=True, + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + ), +) +class TaxpayerData: + start_of_activities_date: Optional[date] + """ + Fecha de inicio de actividades. + """ + economic_activities: str + """ + Actividades Económicas + """ + tax_category: str + """ + Categoría Tributaria + """ + address: str + """ + Domicilio + """ + branches: Sequence[str] + """ + Sucursales + """ + last_filed_documents: Sequence[LastFiledDocument] + """ + Últimos documentos timbrados + """ + tax_observations: Optional[str] = None + """ + Observaciones tributarias + """ + + +@pydantic.dataclasses.dataclass( + frozen=True, +) +class LastFiledDocument: + name: str + date: date diff --git a/src/cl_sii/cte/parsers.py b/src/cl_sii/cte/parsers.py index bbcd6a5a..5242cfef 100644 --- a/src/cl_sii/cte/parsers.py +++ b/src/cl_sii/cte/parsers.py @@ -1,8 +1,10 @@ from __future__ import annotations +from datetime import datetime + from bs4 import BeautifulSoup -from .data_models import LegalRepresentative, TaxpayerProvidedInfo +from .data_models import LastFiledDocument, LegalRepresentative, TaxpayerData, TaxpayerProvidedInfo def parse_taxpayer_provided_info(html_content: str) -> TaxpayerProvidedInfo: @@ -89,3 +91,88 @@ def parse_taxpayer_provided_info(html_content: str) -> TaxpayerProvidedInfo: company_formation=company_formation, participation_in_existing_companies=participation_in_companies, ) + + +def parse_taxpayer_data(html_content: str) -> TaxpayerData: + """ + Parse the CTE HTML content to extract the content of the section: + "Datos del Contribuyente" + + Args: + html_content: HTML string containing the taxpayer information table + + Returns: + TaxpayerData instance with the parsed data + """ + soup = BeautifulSoup(html_content, 'html.parser') + table = soup.find('table', id='tbl_dbcontribuyente') + if not table: + raise ValueError("Could not find 'Datos del Contribuyente' table in HTML") + + fecha_inicio_elem = table.find(id='td_fecha_inicio') # type: ignore[attr-defined] + if fecha_inicio_elem: + start_of_activities_date = ( + datetime.strptime(fecha_inicio_elem.get_text(strip=True), "%d-%m-%Y").date() + if fecha_inicio_elem.get_text(strip=True) + else None + ) + else: + start_of_activities_date = None + + actividades_elem = table.find(id='td_actividades') # type: ignore[attr-defined] + if actividades_elem: + economic_activities = actividades_elem.get_text(separator="\n", strip=True) + else: + economic_activities = "" + + categoria_elem = table.find(id='td_categoria') # type: ignore[attr-defined] + if categoria_elem: + tax_category = categoria_elem.get_text(strip=True) + else: + tax_category = "" + + domicilio_elem = table.find(id='td_domicilio') # type: ignore[attr-defined] + if domicilio_elem: + address = domicilio_elem.get_text(strip=True) + else: + address = "" + + # Sucursales + branches = [] + sucursales_row = table.find( # type: ignore[attr-defined] + 'td', + string=lambda s: s and 'Sucursales:' in s, + ) + if sucursales_row: + sucursales_td = sucursales_row.find_next_sibling('td') + if sucursales_td: + branches_text = sucursales_td.get_text(separator="\n", strip=True) + branches = [b for b in branches_text.split("\n") if b] + + # Últimos documentos timbrados + last_filed_documents = [] + tim_nombre_elem = table.find(id='td_tim_nombre') # type: ignore[attr-defined] + tim_fecha_elem = table.find(id='td_tim_fecha') # type: ignore[attr-defined] + if tim_nombre_elem and tim_fecha_elem: + names = tim_nombre_elem.get_text(separator="\n", strip=True).split("\n") + dates = tim_fecha_elem.get_text(separator="\n", strip=True).split("\n") + for name, date_str in zip(names, dates): + if name and date_str: + doc_date = datetime.strptime(date_str, "%d-%m-%Y").date() + last_filed_documents.append(LastFiledDocument(name=name, date=doc_date)) + + # Observaciones tributarias + tax_observations = None + observaciones_elem = table.find(id='td_observaciones') # type: ignore[attr-defined] + if observaciones_elem: + tax_observations = observaciones_elem.get_text(strip=True) + + return TaxpayerData( + start_of_activities_date=start_of_activities_date, + economic_activities=economic_activities, + tax_category=tax_category, + address=address, + branches=branches, + last_filed_documents=last_filed_documents, + tax_observations=tax_observations, + ) diff --git a/src/tests/test_cte_parsers.py b/src/tests/test_cte_parsers.py index c2fb8005..ab1c5d4c 100644 --- a/src/tests/test_cte_parsers.py +++ b/src/tests/test_cte_parsers.py @@ -1,5 +1,6 @@ from __future__ import annotations +from datetime import date from unittest import TestCase from cl_sii.cte import data_models, parsers @@ -49,3 +50,58 @@ def test_parse_taxpayer_provided_info(self) -> None: assert_raises_cm.exception.args, ("Could not find taxpayer information table in HTML",), ) + + def test_parse_taxpayer_data(self) -> None: + html_content = read_test_file_str_utf8('test_data/sii-cte/cte_empty_f29.html') + with self.subTest("Parsing ok"): + result = parsers.parse_taxpayer_data(html_content) + expected_obj = data_models.TaxpayerData( + start_of_activities_date=date(2023, 11, 15), + economic_activities=( + "SERVICIOS DE ASESORIA Y CONSULTORIA EN MATERIA DE ADMINISTRACION DE EMPRESAS " + "Y OTROS SERVICIOS DE ASESORIA ADMINISTRATIVA Y DE NEGOCIOS N.C.P.\n" + "ACTIVIDADES DE OTRAS ORGANIZACIONES EMPRESARIALES N.C.P.\n" + "OTRAS ACTIVIDADES DE SERVICIOS PERSONALES N.C.P." + ), + tax_category="Primera categoría", + address="AV REAL, LAS CONDES", + branches=[], + last_filed_documents=[ + data_models.LastFiledDocument( + name="FACTURA ELECTRONICA", date=date(2025, 7, 24) + ), + data_models.LastFiledDocument( + name="FACTURA NO AFECTA O EXENTA ELECTRONICA", date=date(2025, 7, 17) + ), + data_models.LastFiledDocument( + name="GUIA DESPACHO ELECTRONICA", date=date(2025, 5, 14) + ), + data_models.LastFiledDocument( + name="NOTA CREDITO ELECTRONICA", date=date(2025, 7, 18) + ), + ], + tax_observations="No tiene observaciones.", + ) + self.assertEqual(result, expected_obj) + + with self.subTest("Parsing empty content"): + with self.assertRaises(ValueError) as assert_raises_cm: + parsers.parse_taxpayer_data("") + self.assertEqual( + assert_raises_cm.exception.args, + ("Could not find 'Datos del Contribuyente' table in HTML",), + ) + + with self.subTest("Parsing content with empty table"): + html_content = read_test_file_str_utf8('test_data/sii-cte/cte_empty_table.html') + result = parsers.parse_taxpayer_data(html_content) + expected_obj = data_models.TaxpayerData( + start_of_activities_date=None, + economic_activities="", + tax_category="", + address="", + branches=[], + last_filed_documents=[], + tax_observations=None, + ) + self.assertEqual(result, expected_obj) diff --git a/src/tests/test_data/sii-cte/cte_empty_f29.html b/src/tests/test_data/sii-cte/cte_empty_f29.html new file mode 100644 index 00000000..e3189952 --- /dev/null +++ b/src/tests/test_data/sii-cte/cte_empty_f29.html @@ -0,0 +1,1141 @@ + + + + + + + + + + + + + + + + +
 
+

Revise los datos contenidos en esta Carpeta Tributaria + Electrónica y, si están correctos, seleccione el botón "Continuar". Si detecta + información incorrecta, reporte esta situación llamando a nuestra Mesa de Ayuda + Telefónica, al (02) 395 11 15, o ingresando a nuestra página Web, www.sii.cl, menú + Contáctenos, opción Problemas y quejas.

+
+ + + + + + + + + + + + + + + + + + + +
+ CARPETA TRIBUTARIA ELECTRÓNICA
PARA SOLICITAR CRÉDITOS
+
+
+
+

+ Importante: Esta información es válida para la fecha y hora en que se generó la carpeta.

+ Toda declaración y pago que sea presentada en papel retrasa la actualización de las bases de datos del SII, por lo que, eventualmente, podrían no aparecer en esta carpeta.
+

+
+
+
+ + + + + + + + + + + + + + + +
Nombre del emisor: INVERSIONES SPA
RUT del emisor:47797573 - 9
Fecha de generación de la carpeta:25/07/2025 07:21
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Datos del Contribuyente +
Fecha de Inicio de Actividades: + 15-11-2023
Actividades Económicas: SERVICIOS DE ASESORIA Y CONSULTORIA EN MATERIA DE ADMINISTRACION DE EMPRESAS Y OTROS SERVICIOS DE ASESORIA ADMINISTRATIVA Y DE NEGOCIOS N.C.P.
ACTIVIDADES DE OTRAS ORGANIZACIONES EMPRESARIALES N.C.P.
OTRAS ACTIVIDADES DE SERVICIOS PERSONALES N.C.P.
+
Categoría tributaria:Primera categoría
Domicilio:AV REAL, LAS CONDES
Sucursales:
Últimos documentos timbrados:FACTURA ELECTRONICA
FACTURA NO AFECTA O EXENTA ELECTRONICA
GUIA DESPACHO ELECTRONICA
NOTA CREDITO ELECTRONICA
+
24-07-2025
17-07-2025
14-05-2025
18-07-2025
+
Observaciones tributarias: + + +

No tiene observaciones.

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Información proporcionada por el contribuyente para fines tributarios (1) +
 Nombre o Razón Social + RUTFecha de Incorporación +
Representante(s) Legal(es)   
 DAVID USUARIO DE PRUEBA76354771-K20-09-2023
 JAVIERA USUARIO DE PRUEBA38855667-620-09-2023 +
Conformación de la sociedad
JAVIERA USUARIO DE PRUEBA38855667-620-09-2023
MARÍA USUARIO DE PRUEBA34413183-k23-02-2024
Participación en sociedades vigentes(2) +    
 - No existen sociedades para el RUT -

(1): Información declarada por el contribuyente y que puede haber sufrido modificaciones. +
(2): La vigencia de estas sociedades está asociada a la existencia de un Inicio de Actividades, sin Término de Giro. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Propiedades y Bienes Raíces (3) +
ComunaRolDirecciónDestinoAvalúo FiscalCuotas vencidas por pagarCuotas vigentes por pagarCondición
(4)
- No se registra información para este RUT - +
+

+ (3): La presente información no acredita dominio de una propiedad. +
+ (4): La condición exento/afecto ha sido determinada de los datos actuales del catastro de Bienes Raíces, considerando las modificaciones recientes de la tasación, y no según la existencia de cuotas de contribuciones emitidas. +
+

+
+
+ + + + + + + + + + + + + + + + + + + + + +
Boletas de Honorarios Electrónicas (5) +
PeríodosHonorario brutoRetención de tercerosPPM de contribuyente
- No se registran Boletas de Honorarios Electrónicas emitidas en los últimos 12 meses - +
+

(5): Además de las Boletas de Honorarios Electrónicas, un contribuyente puede tener boletas de honorarios emitidas en papel, cuyo detalle no está disponible en forma electrónica. +

+
+
+
+ + + + + + + + + + + + + + + + +
Declaraciones de IVA (F29) +
JULIO 20251 / 1
+
+

+ - No se registra declaración para este período - +

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Declaraciones de Renta (F22) +
Año Tributario 20251 / 3
+
+

+ - No se registra declaración para este período - +

+ + +
+
Año Tributario 20242 / 3
+
+
+
+
+ + + + + + + + +
+ + + + + + + + + + +
REPUBLICA DE CHILE
+ SERVICIO DE IMPUESTOS INTERNOS
+
FORM. 22 +
+

AÑO TRIBUTARIO + + 2024

+ IMPUESTOS ANUALES A LA RENTA + +
  07   +   + + + 329526074 + +
+ + + + + + + + + + + + + + + + + + +
ROL UNICO
+ TRIBUTARIO
+
01 + Apellido Paterno
+ o razón social
+
02 + Apellido Materno + 05 + Nombres +
03 + + + 76706365-2 + + INVERSIONES EL LIBANO SPA +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
06 + Calle    Nº         Of.Depto.    + 09 + Teléfono + 08 + Comuna +
  + + I LA CATOLICA 4196 null +   +   +   + + LAS CONDES +
13Actividad, profesión o giro del negocio14Código actividad económica903RUT. del Representante
  + + DESRATIZACION, DESINFECCION Y EXTERMINIO DE + PLAGAS NO AGRICOLAS +   + + 812901 +   + + 11738446-2 +
55Correo + Electrónico +
  + + roberto.opazo@xinerlink.cl +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
15Fecha + Vencimiento Declaración 04202418Base + imponible IDPC de empresas acogidas al régimen Pro Pyme, según art. + 14 letra D) N° 3 LIR 926
20IDPC + de empresas acogidas al régimen Pro Pyme, según art. 14 letra D) N° + 3 LIR 9336PPM + y remanente del IEAM 72300
53Región  + 1355Correo + Electrónico  + roberto.opazo@xinerlink.cl +
102Capital + Efectivo 10000000122Total + del Activo 11591162
123Total + del Pasivo 11590236301Nombre + Institución Bancaria BANCO ESTADO
305RESULTADO + LIQUIDACIÓN ANUAL IMPUESTO A LA RENTA (si el resultado es negativo o + cero, deberá declarar por Internet) -72207306Numero + de Cuenta 20470150531
315Fecha + Presentación 24/04/2024645CPT + positivo final 0
646CPT + negativo final 0780Tipo + de Cuenta C
784Saldo + cuenta corriente bancaria según, conciliación 111531521400Ingresos + del giro percibidos 7230000
1409Existencias, + insumos y servicios del negocio, pagados 15340741410Total + de ingresos anuales 7230000
1415Arriendos + pagados 45000001424Otros + gastos deducibles de los ingresos 1195000
1430Total + de egresos anuales 72290741440Base + Imponible afecta a IDPC (o pérdida tributaria antes de imputar + dividendos o retiros percibidos) del ejercicio 926
1494Capital + aportado, histórico (incluye aumentos y disminuciones efectivas)  + 100000001545CPTS + positivo final 926
1703CPTS + positivo 9261705Base + imponible afecta a IDPC del ejercicio 926
1720Subtotal  + 9261729Base + imponible antes de rebaja por incentivo al ahorro (art. 14 letra E) + LIR) y/o por pago de IDPC voluntario (art. 14 letra A) N°6 LIR y + art. 42° transitorio Ley N° 21.210) o pérdida + tributaria 926
1904a) + PPM arts. 84 letras a), c) , e), y h) y 14 D N° 3 letra (k) LIR  + 723008809Rut + Representante 767063652
8811Moneda + de la Declaración CLP8865Código + Emisión 3
99342023 + comienza registro de layout f22 (rnet) F22.3
+ + + + + + + + + + +
Folio Nº + + + 329526074 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
REMANENTE DE + CREDITO
66SALDO A FAVOR + + 85 + + 72207 + +
67Menos: Saldo puesto a + disposición +
de los socios (Según Recuadro N° 6) + + . +
86 +   + -
68DEVOLUCIÓN SOLICITADA + + 87 + + + 72207 + =
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IMPUESTO A PAGAR +
69Impuesto Adeudado90 +   + +
70Reajuste Art. 72 línea + 69: 0.9% + 39 +   + +
71TOTAL A PAGAR (Líneas + 69+70) + + 91 +   + =
RECARGOS POR + DECLARACIÓN
+ FUERA DE PLAZO
+
(RECARGOS POR MORA EN EL PAGO) +
72MAS: Reajustes + declaración fuera de plazo + 92  +   + +
73MAS: Intereses y Multas + declaración fuera de plazo + 93  +   + +
74TOTAL A PAGAR (Líneas + 71+72+73)94  +   + + =
+
+ + + + + + +
+ + Declaro bajo juramento que la información contenida en este + documento es la expresión + fiel de la verdad, por lo que asumo la responsabilidad + correspondiente. + +
+
+
+ +
+ + +
+
+
Año Tributario 20233 / 3
+
+

+ - No se registra declaración para este período - +

+ + +
+
+ + + + + + + +
+
+ + + + + + +
+ + +
+ + + + diff --git a/src/tests/test_data/sii-cte/cte_empty_table.html b/src/tests/test_data/sii-cte/cte_empty_table.html new file mode 100644 index 00000000..9a96da86 --- /dev/null +++ b/src/tests/test_data/sii-cte/cte_empty_table.html @@ -0,0 +1,511 @@ + + + + + + + + + + + + + + + + +
 
+

Revise los datos contenidos en esta Carpeta Tributaria + Electrónica y, si están correctos, seleccione el botón "Continuar". Si detecta + información incorrecta, reporte esta situación llamando a nuestra Mesa de Ayuda + Telefónica, al (02) 395 11 15, o ingresando a nuestra página Web, www.sii.cl, menú + Contáctenos, opción Problemas y quejas.

+
+ + + + + + + + + + + + + + + + + + + +
+ CARPETA TRIBUTARIA ELECTRÓNICA
PARA SOLICITAR CRÉDITOS
+
+
+
+

+ Importante: Esta información es válida para la fecha y hora en que se generó la carpeta.

+ Toda declaración y pago que sea presentada en papel retrasa la actualización de las bases de datos del SII, por lo que, eventualmente, podrían no aparecer en esta carpeta.
+

+
+
+
+ + + + + + + + + + + + + + + +
Nombre del emisor: INVERSIONES SPA
RUT del emisor:47797573 - 9
Fecha de generación de la carpeta:25/07/2025 07:21
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Datos del Contribuyente +
Fecha de Inicio de Actividades: +
Actividades Económicas:
Categoría tributaria:
Domicilio:
Sucursales:
Últimos documentos timbrados:
Observaciones tributarias:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Información proporcionada por el contribuyente para fines tributarios (1) +
 Nombre o Razón Social + RUTFecha de Incorporación +
Representante(s) Legal(es)   
    
    
Conformación de la sociedad
   
   
Participación en sociedades vigentes(2) +    
 - No existen sociedades para el RUT -

(1): Información declarada por el contribuyente y que puede haber sufrido modificaciones. +
(2): La vigencia de estas sociedades está asociada a la existencia de un Inicio de Actividades, sin Término de Giro. +

+
+ + + + + + + + + + + + + + + + + + + + + + +
Propiedades y Bienes Raíces (3) +
ComunaRolDirecciónDestinoAvalúo FiscalCuotas vencidas por pagarCuotas vigentes por pagarCondición
(4)
- No se registra información para este RUT - +
+

+ (3): La presente información no acredita dominio de una propiedad. +
+ (4): La condición exento/afecto ha sido determinada de los datos actuales del catastro de Bienes Raíces, considerando las modificaciones recientes de la tasación, y no según la existencia de cuotas de contribuciones emitidas. +
+

+
+
+ + + + + + + + + + + + + + + + + + +
Boletas de Honorarios Electrónicas (5) +
PeríodosHonorario brutoRetención de tercerosPPM de contribuyente
- No se registran Boletas de Honorarios Electrónicas emitidas en los últimos 12 meses - +
+

(5): Además de las Boletas de Honorarios Electrónicas, un contribuyente puede tener boletas de honorarios emitidas en papel, cuyo detalle no está disponible en forma electrónica. +

+
+
+
+ + + + + + + + + + + + + + + + +
Declaraciones de IVA (F29) +
JULIO 20251 / 1
+
+

+ - No se registra declaración para este período - +

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Declaraciones de Renta (F22) +
Año Tributario 20251 / 3
+
+

+ - No se registra declaración para este período - +

+ + +
+
Año Tributario 20242 / 3
+
+

+ - No se registra declaración para este período - +

+ + +
+
Año Tributario 20233 / 3
+
+

+ - No se registra declaración para este período - +

+ + +
+
+ + + + + + + +
+
+ + + + + + +
+ + +
+ + + +