Metadata-Version: 2.5
Name: pytiss
Version: 1.0.0
Summary: Implementação em Python do padrão brasileiro TISS
Author: Luis Bezerra
License: MIT
License-File: LICENSE
Keywords: ans,saude-suplementar,tiss,xml,xsd
Requires-Python: >=3.11
Requires-Dist: xmlschema<5,>=3.4
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.2; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

# pytiss

`pytiss` é uma biblioteca Python para criar, serializar e validar mensagens do padrão TISS brasileiro.

A versão inicial tem foco em geração de XML `mensagemTISS`, validação offline contra os XSD oficiais da ANS e uma API pequena o suficiente para evoluir com segurança.

## Recursos

- TISS Componente de Comunicação `04.03.00`.
- Guia de Consulta com modelo de domínio dedicado.
- Guia SP/SADT de execução com modelo de domínio dedicado.
- Demais guias de lote por payload validável: Resumo de Internação, Honorários e Odontologia.
- Solicitações de autorização, anexos clínicos, recurso de glosa, demonstrativos, elegibilidade, cancelamento, status e envio de documentos por payload genérico validado por XSD.
- Serialização XML determinística em `mensagemTISS`.
- Validação offline com os arquivos XSD oficiais empacotados em `pytiss.schemas`.
- Tipagem estática com `py.typed`.

## Instalação

```bash
pip install pytiss
```

Para desenvolvimento local:

```bash
pip install -e ".[dev]"
```

## Conceitos Principais

`MensagemContexto` representa o cabeçalho comum da transação TISS, incluindo origem, destino, lote, sequencial, data e hora.

`Consulta` e `SPSADT` são modelos de domínio dedicados para os fluxos mais comuns da primeira versão.

`MensagemTISS` permite montar qualquer fluxo de prestador para operadora usando um dicionário que segue exatamente a estrutura do XSD.

`LoteGuias` e `GuiaTISS` permitem enviar tipos de guia ainda não modelados como dataclasses dedicadas.

`XSDValidator` valida XML em memória ou arquivos usando os schemas oficiais empacotados.

## Guia De Consulta

```python
from datetime import date, time
from decimal import Decimal

from pytiss import (
    Beneficiario,
    Consulta,
    Contratado,
    MensagemContexto,
    Procedimento,
    Profissional,
    TISSVersion,
)

contexto = MensagemContexto(
    registro_ans="123456",
    numero_lote="1",
    prestador_origem=Contratado(codigo_prestador_na_operadora="123"),
    sequencial_transacao="1",
    data_registro_transacao=date(2026, 1, 1),
    hora_registro_transacao=time(12, 0),
)

guia = Consulta(
    contexto=contexto,
    numero_guia_prestador="000001",
    beneficiario=Beneficiario(numero_carteira="1234567890"),
    contratado_executante=Contratado(codigo_prestador_na_operadora="123"),
    cnes="1234567",
    profissional_executante=Profissional(
        conselho_profissional="06",
        numero_conselho_profissional="123456",
        uf="35",
        cbos="225125",
    ),
    data_atendimento=date(2026, 1, 1),
    procedimento=Procedimento(
        codigo_tabela="22",
        codigo="10101012",
        valor_unitario=Decimal("100.00"),
    ),
)

resultado = guia.validate(version=TISSVersion.V4_03_00)
if resultado.is_valid:
    xml = guia.to_xml(version=TISSVersion.V4_03_00, validate=True)
```

## Guia SP/SADT

```python
from datetime import date
from decimal import Decimal

from pytiss import Beneficiario, Contratado, Procedimento, Profissional, SPSADT

procedimento = Procedimento(
    codigo_tabela="22",
    codigo="10101012",
    descricao="Consulta em consultório",
    quantidade=Decimal("1"),
    valor_unitario=Decimal("100.00"),
    sequencial_item=1,
    data_execucao=date(2026, 1, 1),
)

guia = SPSADT(
    contexto=contexto,
    numero_guia_prestador="000002",
    beneficiario=Beneficiario(numero_carteira="1234567890"),
    contratado_solicitante=Contratado(codigo_prestador_na_operadora="123"),
    nome_contratado_solicitante="Prestador Sintético",
    profissional_solicitante=Profissional(
        conselho_profissional="06",
        numero_conselho_profissional="123456",
        uf="35",
        cbos="225125",
        nome_profissional="Médico Sintético",
    ),
    contratado_executante=Contratado(codigo_prestador_na_operadora="123"),
    cnes="1234567",
    data_solicitacao=date(2026, 1, 1),
    procedimentos=(procedimento,),
)

xml = guia.to_xml(validate=True)
```

## Entrada Por Dicionário

Os modelos dedicados também aceitam entrada por `dict`, útil para integrações com APIs ou formulários.

```python
from pytiss import Consulta

guia = Consulta.from_dict(
    {
        "contexto": {
            "registro_ans": "123456",
            "numero_lote": "1",
            "prestador_origem": {"codigo_prestador_na_operadora": "123"},
            "sequencial_transacao": "1",
            "data_registro_transacao": "2026-01-01",
            "hora_registro_transacao": "12:00:00",
        },
        "numero_guia_prestador": "000001",
        "beneficiario": {"numero_carteira": "1234567890", "atendimento_rn": False},
        "contratado_executante": {"codigo_prestador_na_operadora": "123"},
        "cnes": "1234567",
        "profissional_executante": {
            "conselho_profissional": "06",
            "numero_conselho_profissional": "123456",
            "uf": "35",
            "cbos": "225125",
        },
        "data_atendimento": "2026-01-01",
        "procedimento": {
            "codigo_tabela": "22",
            "codigo": "10101012",
            "valor_unitario": "100.00",
        },
    }
)
```

## Fluxos Genéricos

Use `MensagemTISS` quando o fluxo já existir no XSD, mas ainda não tiver um modelo de domínio dedicado.

As chaves do dicionário em `dados` devem usar os nomes exatos das tags do XSD e devem respeitar a ordem definida pelo esquema.

```python
from pytiss import FluxoTISS, MensagemTISS

mensagem = MensagemTISS(
    contexto=contexto,
    fluxo=FluxoTISS.VERIFICA_ELEGIBILIDADE,
    dados={
        "dadosPrestador": {"codigoPrestadorNaOperadora": "123"},
        "numeroCarteira": "1234567890",
    },
)

xml = mensagem.to_xml(validate=True)
```

Fluxos disponíveis em `FluxoTISS`:

- `ENVIO_LOTE_GUIAS`
- `ENVIO_ANEXO`
- `SOLICITACAO_DEMONSTRATIVO_RETORNO`
- `SOLICITACAO_STATUS_PROTOCOLO`
- `SOLICITACAO_PROCEDIMENTO`
- `SOLICITA_STATUS_AUTORIZACAO`
- `VERIFICA_ELEGIBILIDADE`
- `CANCELA_GUIA`
- `COMUNICACAO_INTERNACAO`
- `RECURSO_GLOSA`
- `SOLICITACAO_STATUS_RECURSO_GLOSA`
- `ENVIO_DOCUMENTOS`

## Guias De Lote Por Payload

`LoteGuias` cobre as guias aceitas por `ctm_guiaLote` no XSD. O lote deve conter guias de um único tipo, com limite de 100 guias por mensagem.

```python
from pytiss import GuiaTISS, LoteGuias, TipoGuia

dados_resumo_internacao = {
    "cabecalhoGuia": {"registroANS": "123456", "numeroGuiaPrestador": "RI1"},
    "numeroGuiaSolicitacaoInternacao": "SI1",
    "dadosAutorizacao": {"dataAutorizacao": "2026-01-01", "senha": "123"},
    "dadosBeneficiario": {"numeroCarteira": "1234567890", "atendimentoRN": "N"},
    "dadosExecutante": {
        "contratadoExecutante": {"codigoPrestadorNaOperadora": "123"},
        "CNES": "1234567",
    },
    "dadosInternacao": {
        "caraterAtendimento": "1",
        "tipoFaturamento": "4",
        "dataInicioFaturamento": "2026-01-01",
        "horaInicioFaturamento": "12:00:00",
        "dataFinalFaturamento": "2026-01-02",
        "horaFinalFaturamento": "12:00:00",
        "tipoInternacao": "1",
        "regimeInternacao": "1",
    },
    "dadosSaidaInternacao": {"indicadorAcidente": "9", "motivoEncerramento": "11"},
    "valorTotal": {"valorTotalGeral": "0.01"},
}

guia = GuiaTISS(tipo=TipoGuia.RESUMO_INTERNACAO, dados=dados_resumo_internacao)
lote = LoteGuias(contexto=contexto, guias=(guia,))

xml = lote.to_xml(validate=True)
```

Tipos disponíveis em `TipoGuia`:

- `SP_SADT`
- `RESUMO_INTERNACAO`
- `HONORARIOS`
- `CONSULTA`
- `ODONTO`

## Validação

`validate()` retorna `ValidationResult`, que contém uma tupla de `ValidationIssue` quando houver erros.

```python
resultado = guia.validate()

if not resultado.is_valid:
    for erro in resultado.errors:
        print(erro.source, erro.code, erro.path, erro.message)
```

Para validar XML já existente:

```python
from pathlib import Path

from pytiss import XSDValidator

validador = XSDValidator()

resultado_xml = validador.validate("<ans:mensagemTISS>...</ans:mensagemTISS>")
resultado_arquivo = validador.validate_file(Path("mensagem.xml"))
```

`validate()` trata strings como conteúdo XML. Para validar um arquivo, use `validate_file()` explicitamente.

## Desenvolvimento

Documentação para contribuidores:

- `CONTRIBUTING.md`: guia de contribuição, qualidade, testes e pull requests.
- `docs/TECHNICAL.md`: arquitetura interna e pontos de extensão.
- `CHANGELOG.md`: histórico de alterações.

Comandos úteis:

```bash
python -m ruff check .
python -m mypy
python -m pytest
```

## Licença

Distribuído sob a licença MIT. Veja `LICENSE`.
