Código fuente para apigatewaycl.api_client.sii.portal_mipyme

#
# API Gateway: Cliente de API en Python.
# Copyright (C) API Gateway <https://www.apigateway.cl>
#
# Este programa es software libre: usted puede redistribuirlo y/o modificarlo
# bajo los términos de la GNU Lesser General Public License (LGPL) publicada
# por la Fundación para el Software Libre, ya sea la versión 3 de la Licencia,
# o (a su elección) cualquier versión posterior de la misma.
#
# Este programa se distribuye con la esperanza de que sea útil, pero SIN
# GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
# PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la GNU Lesser General
# Public License (LGPL) para obtener una información más detallada.
#
# Debería haber recibido una copia de la GNU Lesser General Public License
# (LGPL) junto a este programa. En caso contrario, consulte
# <http://www.gnu.org/licenses/lgpl.html>.
#

"""
Módulo para consultas al Portal MIPYME del SII.

Para más información sobre la API, consulte la `documentación completa del
Portal MIPYME <https://www.apigateway.cl/docs>`_.
"""

from __future__ import annotations

from abc import ABC
from typing import Any, ClassVar

from .. import ApiBase, ApiResponse


[documentos] class PortalMipyme(ApiBase, ABC): """ Base para los clientes específicos del Portal Mipyme. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """ def __init__( self, identificador: str, clave: str, **kwargs: str, ) -> None: """Autentica con `identificador`/`clave` del contribuyente.""" super().__init__( identificador=identificador, clave=clave, **kwargs, # type: ignore[arg-type] )
[documentos] class Contribuyentes(PortalMipyme): """ Cliente para los endpoints de contribuyentes del Portal Mipyme. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """
[documentos] def info( self, contribuyente: str, emisor: str, dte: int = 33, ) -> ApiResponse[dict[str, Any]]: """ Obtiene información de un contribuyente específico. Respuesta (ejemplo):: { "data": { "rut": 76192083, "dv": "9", "razon_social": "API Gateway", "direcciones": [ { "direccion": "Daniel Barros Grez 191", "comuna": "SANTA CRUZ", "ciudad": "SANTA CRUZ", "telefono": null } ], "actividades": [ { "codigo": "619090", "glosa": "OTRAS ACTIVIDADES DE TELECOMUNICACIONES" } ] }, "metadata": {"timestamp": "..."} } :param str contribuyente: RUT del contribuyente. :param str emisor: RUT del emisor del DTE. :param int dte: Tipo de DTE. :return: Respuesta de la API, con `data` y `metadata`. En `data`, datos del contribuyente. :rtype: ApiResponse[dict[str, Any]] """ url = ( '/sii/mipyme/contribuyentes/info/' '%(contribuyente)s/%(emisor)s/%(dte)s' % {'contribuyente': contribuyente, 'emisor': emisor, 'dte': dte} ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] class Dte(PortalMipyme): """ Base para los clientes específicos de DTE del Portal Mipyme. Incluye constantes para diferentes estados de DTE. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """ ESTADO_EMITIDO = 'EMI' # Documento emitido ESTADO_BORRADOR = 'PRV' # Borrador (pre-view) de documento ESTADO_CERTIFICADO_RECHAZADO = 'DCD' # Certificado rechazado ESTADO_EMISOR_INVALIDO = 'DEI' # RUT emisor inválido ESTADO_FOLIO_INVALIDO = 'DFI' # Folio DTE inválido ESTADO_INCOMPLETO = 'DIN' # Incompleto ESTADO_FIRMA_SIN_PERMISO = 'DPF' # Sin permiso de firma ESTADO_FIRMA_RECHAZADA = 'DRF' # DTE rechazado por firma ESTADO_RECEPTOR_INVALIDO = 'DRI' # RUT receptor inválido ESTADO_REPETIDO = 'DRR' # Rechazado por repetido ESTADO_INICIALIZADO = 'INI' # DTE inicializado ESTADO_ACEPTADO = 'RAC' # DTE aceptado por receptor ESTADO_DISCREPANCIAS = 'RAD' # DTE aceptado con discrepancias ESTADO_NO_RECIBIDO = 'RNR' # DTE no recibido por receptor ESTADO_RECIBIDO = 'RRC' # DTE recibido por receptor ESTADO_ACEPTADO_LEY_19983 = 'RAL' # DTE aceptado Ley 19.983 ESTADO_RECHAZADO_RECEPTOR = 'RRH' # DTE rechazado por receptor ESTADO_SIN_REPAROS = 'RSR' # Recibido sin reparos DTE_TIPOS: ClassVar[dict[str, str]] = { '33': '33', '34': '34', '35': '35', '36': '36', '37': '37', '38': '38', '39': '39', }
[documentos] def get_codigo_dte(self, tipo: str) -> str: """ Obtiene el código correspondiente al tipo de DTE. :param str tipo: Tipo de DTE. :return: Código del DTE. :rtype: str """ if tipo in self.DTE_TIPOS: return self.DTE_TIPOS[tipo] return tipo.replace(' ', '-')
[documentos] class Borradores(Dte): """ Cliente para documentos borradores del Portal Mipyme. Un borrador es un DTE armado en el Portal MIPYME del SII, antes de convertirse en un documento emitido de verdad. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """
[documentos] def documentos( self, emisor: str, filtros: dict[str, Any] | None = None, ) -> ApiResponse[list[dict[str, Any]]]: """ Listado de documentos borradores del emisor. Por defecto entrega todos los borradores, ordenados por fecha ascendente. Para paginar se indica `NUM_PAG` en `filtros` (100 borradores por página) y `metadata.n_paginas` dice cuántas páginas hay con los filtros usados. Filtros disponibles: `NUM_PAG`, `CODIGO` (código del borrador), `TPO_DOC` (código de DTE), `RUT_RECP` (RUT del receptor, con o sin dígito verificador), `RZN_SOC` (razón social del receptor, o parte de ella), `FEC_DESDE` y `FEC_HASTA` (AAAA-MM-DD) y `ORDEN` (`FecAsc` o `FecDesc`). Respuesta (ejemplo):: { "data": [ { "ehdr_codigo": "4728198", "ptdc_codigo": "33", "ptdc_codigo_desc": "Factura Electronica", "ptes_estado": null, "efxp_fch_emis": "2026-09-23 18:49:38", "efxp_rzn_soc": "EMPRESA EMISORA SPA", "efxp_rut_recep": "76192083", "efxp_dv_recep": "9", "...": "..." } ], "metadata": { "timestamp": "...", "n_documentos": 1, "n_paginas": 1 } } :param str emisor: RUT del emisor de los documentos. :param dict filtros: Filtros de búsqueda (opcional). :return: Respuesta de la API, con `data` y `metadata`. En `data`, listado de borradores con los campos del Portal MIPYME en minúsculas; el código del borrador es `ehdr_codigo`. :rtype: ApiResponse[list[dict[str, Any]]] """ url = '/sii/mipyme/borradores/documentos/%(emisor)s' % { 'emisor': emisor, } body = {'auth': self._get_auth(), 'filtros': filtros or {}} response = self.client.post(url, data=body) return self._json(response)
[documentos] def emitir(self, dte: dict[str, Any]) -> ApiResponse[dict[str, Any]]: """ Crea un documento borrador en el Portal Mipyme. No emite un DTE real ante el SII — el borrador se confirma luego desde el propio Portal MIPYME. Respuesta (ejemplo):: { "data": { "ehdr_codigo": "4728198", "ptdc_codigo": "33", "ptdc_codigo_desc": "Factura Electronica", "ptes_estado": null, "efxp_fch_emis": "2026-09-23 18:49:38", "efxp_rzn_soc": "EMPRESA EMISORA SPA", "efxp_rut_recep": "76192083", "efxp_dv_recep": "9", "...": "..." }, "metadata": {"timestamp": "..."} } :param dict dte: Datos del DTE a armar como borrador. :return: Respuesta de la API, con `data` y `metadata`. En `data`, el borrador creado. :rtype: ApiResponse[dict[str, Any]] """ body = {'auth': self._get_auth(), 'dte': dte} response = self.client.post('/sii/mipyme/borradores/emitir', data=body) return self._json(response)
[documentos] def eliminar( self, emisor: str, codigo: str, ) -> ApiResponse[dict[str, Any]]: """ Elimina un documento borrador del Portal Mipyme. Respuesta (ejemplo):: { "data": { "ehdr_codigo": "4728198", "ptdc_codigo": "33", "ptdc_codigo_desc": "Factura Electronica", "ptes_estado": null, "efxp_fch_emis": "2026-09-23 18:49:38", "efxp_rzn_soc": "EMPRESA EMISORA SPA", "efxp_rut_recep": "76192083", "efxp_dv_recep": "9", "...": "..." }, "metadata": {"timestamp": "..."} } :param str emisor: RUT del emisor de los documentos. :param str codigo: Código del borrador (del listado de documentos). :return: Respuesta de la API, con `data` y `metadata`. En `data`, `True` si el borrador fue eliminado. :rtype: ApiResponse[dict[str, Any]] """ url = '/sii/mipyme/borradores/eliminar/%(emisor)s/%(codigo)s' % { 'emisor': emisor, 'codigo': codigo, } body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] class DteEmitidos(Dte): """ Cliente específico para gestionar DTE emitidos en el Portal Mipyme. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """
[documentos] def documentos( self, emisor: str, filtros: dict[str, Any] | None = None, ) -> ApiResponse[list[dict[str, Any]]]: """ Obtiene documentos de DTE emitidos por un emisor. Respuesta (ejemplo):: { "data": [ { "codigo": 12345678, "rut": 76192083, "dv": "9", "razon_social": "CLIENTE EJEMPLO", "dte": 33, "dte_glosa": "Factura Electrónica", "folio": 123, "fecha": "15/11/2024", "...": "..." } ], "metadata": { "timestamp": "...", "n_documentos": 1926, "n_paginas": 20 } } :param str emisor: RUT del emisor. :param dict filtros: Filtros adicionales para la consulta. :return: Respuesta de la API, con `data` y `metadata`. En `data`, documentos de DTE emitidos. :rtype: ApiResponse[list[dict[str, Any]]] """ url = '/sii/mipyme/emitidos/documentos/%(emisor)s' % {'emisor': emisor} body = {'auth': self._get_auth(), 'filtros': filtros or {}} response = self.client.post(url, data=body) return self._json(response)
[documentos] def pdf(self, emisor: str, codigo: str) -> bytes: """ Obtiene el PDF de un DTE emitido. El documento se identifica por su `codigo`, que entrega el listado de `documentos()`; no por tipo de DTE y folio. :param str emisor: RUT del emisor. :param str codigo: Código del DTE emitido. :return: Contenido del PDF del DTE emitido. :rtype: bytes """ url = '/sii/mipyme/emitidos/pdf/%(emisor)s/%(codigo)s' % { 'emisor': emisor, 'codigo': codigo, } body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content
[documentos] def xml( self, emisor: str, dte: str, folio: str, fecha_emision: str, ) -> bytes: """ Obtiene el XML de un DTE emitido. :param str emisor: RUT del emisor. :param str dte: Tipo de DTE. :param str folio: Número de folio del DTE. :param str fecha_emision: Fecha de emisión del documento (AAAA-MM-DD). Sin ella el Portal MIPYME no ubica el documento y la API no entrega el XML. :return: Contenido del XML del DTE emitido, sin decodificar. :rtype: bytes """ url = self._build_url( '/sii/mipyme/emitidos/xml/%(emisor)s/%(dte)s/%(folio)s' % {'emisor': emisor, 'dte': dte, 'folio': folio}, fecha_emision=fecha_emision, ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content
[documentos] class DteRecibidos(Dte): """ Cliente para DTE recibidos en el Portal Mipyme. Proporciona métodos para obtener documentos, PDF y XML de DTE recibidos. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """
[documentos] def documentos( self, receptor: str, filtros: dict[str, Any] | None = None, ) -> ApiResponse[list[dict[str, Any]]]: """ Obtiene documentos de DTE recibidos por un receptor. Respuesta (ejemplo):: { "data": [ { "codigo": 12345678, "rut": 76192083, "dv": "9", "razon_social": "CLIENTE EJEMPLO", "dte": 33, "dte_glosa": "Factura Electrónica", "folio": 123, "fecha": "15/11/2024", "...": "..." } ], "metadata": { "timestamp": "...", "n_documentos": 589, "n_paginas": 6 } } :param str receptor: RUT del receptor. :param dict filtros: Filtros adicionales para la consulta. :return: Respuesta de la API, con `data` y `metadata`. En `data`, documentos de DTE recibidos. :rtype: ApiResponse[list[dict[str, Any]]] """ url = '/sii/mipyme/recibidos/documentos/%(receptor)s' % { 'receptor': receptor } body = {'auth': self._get_auth(), 'filtros': filtros or {}} response = self.client.post(url, data=body) return self._json(response)
[documentos] def pdf(self, receptor: str, codigo: str) -> bytes: """ Obtiene el PDF de un DTE recibido. El documento se identifica por su `codigo`, que entrega el listado de `documentos()`; no por emisor, tipo de DTE y folio. :param str receptor: RUT del receptor. :param str codigo: Código del DTE recibido. :return: Contenido del PDF del DTE recibido. :rtype: bytes """ url = '/sii/mipyme/recibidos/pdf/%(receptor)s/%(codigo)s' % { 'receptor': receptor, 'codigo': codigo, } body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content
[documentos] def xml( self, receptor: str, emisor: str, dte: str, folio: str, fecha_emision: str, ) -> bytes: """ Obtiene el XML de un DTE recibido. :param str receptor: RUT del receptor. :param str emisor: RUT del emisor. :param str dte: Tipo de DTE. :param str folio: Número de folio del DTE. :param str fecha_emision: Fecha de emisión del documento (AAAA-MM-DD). Sin ella el Portal MIPYME no ubica el documento y la API no entrega el XML. :return: Contenido del XML del DTE recibido, sin decodificar. :rtype: bytes """ url = self._build_url( '/sii/mipyme/recibidos/xml/' '%(receptor)s/%(emisor)s/%(dte)s/%(folio)s' % { 'receptor': receptor, 'emisor': emisor, 'dte': dte, 'folio': folio, }, fecha_emision=fecha_emision, ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content