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

#
# 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 Boletas de Honorarios Electrónicas, emitidas y recibidas.

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

from __future__ import annotations

from typing import Any

from .. import ApiBase, ApiResponse


[documentos] class BheEmitidas(ApiBase): """ Cliente para Boletas de Honorarios Electrónicas (BHE) emitidas. Provee métodos para emitir, anular, y consultar información relacionada con BHEs. :param str identificador: Identificador del contribuyente. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """ # Quién debe hacer la retención asociada al honorario para pagar al SII RETENCION_RECEPTOR = 1 RETENCION_EMISOR = 2 # Posibles motivos de anulación de una BHE ANULACION_CAUSA_SIN_PAGO = 1 ANULACION_CAUSA_SIN_PRESTACION = 2 ANULACION_CAUSA_ERROR_DIGITACION = 3 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] def documentos( self, emisor: str, periodo: str, pagina: int = 1, ) -> ApiResponse[dict[str, Any]]: """ Obtiene los documentos de BHE emitidos por un emisor en un periodo. La API exige `pagina`: parte en `1` y se avanza de a una, hasta `n_paginas`. Respuesta (ejemplo):: { "data": { "boletas": [ { "numero": 144, "rut": 0, "dv": "0", "nombre": "", "fecha": "2025-08-18", "...": "..." } ], "n_paginas": 1, "n_boletas": 1 }, "metadata": {"timestamp": "..."} } :param str emisor: RUT del emisor de las boletas. :param str periodo: Período de tiempo de las boletas emitidas. :param int pagina: Página a consultar, partiendo desde `1`. :return: Respuesta de la API, con `data` y `metadata`. En `data`, documentos de BHE. :rtype: ApiResponse[dict[str, Any]] """ url = self._build_url( '/sii/bhe/emitidas/documentos/%(emisor)s/%(periodo)s' % {'emisor': emisor, 'periodo': periodo}, pagina=pagina, ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] def emitir(self, boleta: dict[str, Any]) -> ApiResponse[dict[str, Any]]: """ Emite una nueva Boleta de Honorarios Electrónica. Respuesta (ejemplo):: { "data": { "Encabezado": { "Emisor": { "CmnaOrigen": "Región", "CodigoDirOrigen": "065722046", "DirOrigen": "Dirección", "GiroEmis": "Giro", "RUTEmisor": "12345678-9", "...": "..." }, "IdDoc": { "CodigoBarras": "Código de barras", "CodigoInferior": "Código inferior", "FchEmis": "2020-09-16", "Folio": 155, "TipoDTE": 66, "...": "..." }, "Receptor": { "CmnaRecep": "Región", "CodigoCmnaRecep": 6205, "CodigoRegionRecep": 6, "DirRecep": "Dirección", "RUTRecep": "0-0", "...": "..." } }, "Detalle": [ { "MontoItem": 50, "NmbItem": "Prueba integracion API Gateway 1" } ] }, "metadata": {"timestamp": "..."} } :param dict boleta: Información detallada de la boleta a emitir. :return: Respuesta de la API, con `data` y `metadata`. En `data`, confirmación de la emisión. :rtype: ApiResponse[dict[str, Any]] """ body = {'auth': self._get_auth(), 'boleta': boleta} response = self.client.post('/sii/bhe/emitidas/emitir', data=body) return self._json(response)
[documentos] def pdf(self, codigo: str) -> bytes: """ Obtiene el PDF de una BHE emitida. :param str codigo: Código único de la BHE. :return: Contenido del PDF de la BHE. :rtype: bytes """ url = '/sii/bhe/emitidas/pdf/%(codigo)s' % {'codigo': codigo} body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content
[documentos] def email( self, codigo: str, email: str, ) -> ApiResponse[dict[str, Any]]: """ Envía por correo electrónico una BHE emitida. Respuesta (ejemplo):: { "data": { "message": "La Boleta de Honorarios Electrónica s...", "email": "ejemplo@ejemplo.com" }, "metadata": {"timestamp": "..."} } :param str codigo: Código único de la BHE a enviar. :param str email: Dirección de correo a la cual enviar la BHE. :return: Respuesta de la API, con `data` y `metadata`. En `data`, confirmación del envío. :rtype: ApiResponse[dict[str, Any]] """ url = '/sii/bhe/emitidas/email/%(codigo)s' % {'codigo': codigo} body = { 'auth': self._get_auth(), 'destinatario': {'email': email}, } response = self.client.post(url, data=body) return self._json(response)
[documentos] def anular( self, emisor: str, folio: str, causa: int = ANULACION_CAUSA_ERROR_DIGITACION, ) -> ApiResponse[dict[str, Any]]: """ Anula una BHE emitida. Respuesta (ejemplo):: { "data": { "boleta_anulada": "S", "dv_autentificado": "4", "dv_receptor": "6", "fecha_cgi": "16/09/2020", "monto_maximo_anulacion": "100000000", "nombre_contribuyente": "EMISOR", "nombre_receptor": "NACIONALES SIN RUT (USO EXCLUSIVO F...", "nro_boleta_eliminar": "155", "...": "..." }, "metadata": {"timestamp": "..."} } :param str emisor: RUT del emisor de la boleta. :param str folio: Número de folio de la boleta. :param int causa: Motivo de anulación de la boleta. :return: Respuesta de la API, con `data` y `metadata`. En `data`, confirmación de la anulación. :rtype: ApiResponse[dict[str, Any]] """ url = ( '/sii/bhe/emitidas/anular/%(emisor)s/%(folio)s?causa=%(causa)s' % {'emisor': emisor, 'folio': folio, 'causa': causa} ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] class BheRecibidas(ApiBase): """ Cliente para Boletas de Honorarios Electrónicas (BHE) recibidas. Provee métodos para obtener documentos, obtener PDF y observar BHE recibidas. :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] def documentos( self, receptor: str, periodo: str, pagina: int = 1, ) -> ApiResponse[dict[str, Any]]: """ Obtiene los documentos de BHE recibidos por un receptor en un periodo. La API exige `pagina`: parte en `1` y se avanza de a una, hasta `n_paginas`. Respuesta (ejemplo):: { "data": { "n_paginas": 1, "n_boletas": 1, "boletas": [ { "anulada": "", "codigo": "", "comuna": "13159", "dv": "9", "estado": "N", "...": "..." } ] }, "metadata": {"timestamp": "..."} } :param str receptor: RUT del receptor de las boletas. :param str periodo: Período de tiempo de las boletas recibidas. :param int pagina: Página a consultar, partiendo desde `1`. :return: Respuesta de la API, con `data` y `metadata`. En `data`, documentos de BHE. :rtype: ApiResponse[dict[str, Any]] """ url = self._build_url( '/sii/bhe/recibidas/documentos/%(receptor)s/%(periodo)s' % {'receptor': receptor, 'periodo': periodo}, pagina=pagina, ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] def pdf(self, codigo: str) -> bytes: """ Obtiene el PDF de una BHE recibida. :param str codigo: Código único de la BHE. :return: Contenido del PDF de la BHE. :rtype: bytes """ url = '/sii/bhe/recibidas/pdf/%(codigo)s' % {'codigo': codigo} body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return response.content
[documentos] def observar( self, emisor: str, numero: str, causa: int = 1, ) -> ApiResponse[dict[str, Any]]: """ Marca una observación en una BHE recibida. Respuesta (ejemplo):: { "data": { "nombre_contribuyente": "API Gateway", "rut_arrastre": "76192083", "dv_arrastre": "9", "fecha_cgi": "16/08/2020", "nro_trx": "12345678" }, "metadata": {"timestamp": "..."} } :param str emisor: RUT del emisor de la boleta. :param str numero: Número de la boleta. :param int causa: Motivo de la observación. :return: Respuesta de la API, con `data` y `metadata`. En `data`, confirmación de la observación. :rtype: ApiResponse[dict[str, Any]] """ url = ( '/sii/bhe/recibidas/observar/%(emisor)s/%(numero)s' '?causa=%(causa)s' % {'emisor': emisor, 'numero': numero, 'causa': causa} ) body = {'auth': self._get_auth()} response = self.client.post(url, data=body) return self._json(response)
[documentos] class ConsultasPorTerceros(ApiBase): """ Cliente para que un tercero verifique la autenticidad de una BHE. A diferencia de `BheEmitidas`/`BheRecibidas`, la autenticación es de quien CONSULTA, no del emisor ni del receptor de la boleta. :param str identificador: Identificador de quien consulta. :param str clave: Clave del identificador. :param kwargs: Argumentos adicionales. """ def __init__( self, identificador: str, clave: str, **kwargs: str, ) -> None: """Autentica con `identificador`/`clave` de quien consulta.""" super().__init__( identificador=identificador, clave=clave, **kwargs, # type: ignore[arg-type] )
[documentos] def verificar( self, codigo_barras: str | None = None, emisor: str | None = None, receptor: str | None = None, periodo: str | None = None, folio: int | None = None, ) -> bytes: """ Verifica la autenticidad de una BHE ante el SII. Modos excluyentes: enviar solo `codigo_barras`, o todos de `emisor`/`receptor`/`periodo`/`folio`. :param str codigo_barras: Código de barras de la boleta. :param str emisor: RUT del emisor de la boleta. :param str receptor: RUT del receptor de la boleta. :param str periodo: Fecha de la boleta (AAAA-MM-DD). :param int folio: Folio de la boleta. :return: PDF oficial de la boleta emitido por el SII. :rtype: bytes """ # Los dos modos son excluyentes: la API rechaza el cuerpo si # llegan mezclados, así que sólo se envía lo que se indicó. criterios = { 'codigo_barras': codigo_barras, 'emisor': emisor, 'receptor': receptor, 'periodo': periodo, 'folio': folio, } body: dict[str, Any] = {'auth': self._get_auth()} body.update( { clave: valor for clave, valor in criterios.items() if valor is not None } ) response = self.client.post( '/sii/bhe/consultas_por_terceros', data=body, ) return response.content