NFS-e/Repasse: Demonstrativo

Endpoints da WebApiAlcance para gerenciar demonstrativos de repasse - os comprovantes (individuais por médico ou consolidados) gerados a partir de um repasse de NFS-e médica. Cobrem criação, listagem com filtros, listagem por repasse, detalhamento por ID, atualização, exclusão e o sub-recurso de histórico de eventos (registro/persistência do ciclo de vida do demonstrativo). O arquivo_url aponta para o PDF gerado do demonstrativo.

Escopo compartilhado nfse-repasse

Este domínio compartilha o primeiro segmento de rota nfse-repasse com os demais recursos de NFS-e/Repasse. O escopo é derivado do primeiro segmento: /nfse-repasse/... vira o recurso nfse_repasse (hífen → underscore). Portanto os escopos são nfse_repasse:read (GET) e nfse_repasse:write (POST/PUT/DELETE) - não existe um escopo demonstrativos:* separado. O mesmo par de escopos vale para todos os recursos sob /api/v1/nfse-repasse/.

Todos os endpoints exigem autenticação. Veja Autenticação para o fluxo de API Key. O Swagger oficial está em api.contabilidadealcance.com.br/docs.

Base path

/api/v1/nfse-repasse/demonstrativos

Escopos necessários

  • nfse_repasse:read - listagens (todas, por repasse), detalhamento e leitura do histórico
  • nfse_repasse:write - criação, atualização, exclusão e registro de eventos no histórico

O escopo é derivado do primeiro segmento da rota: /nfse-repasse vira o recurso nfse_repasse (hífen → underscore), e o método HTTP define a ação (read para GET, write para POST/PUT/DELETE).

Camada single-company: perfil administrador nas escritas

Nesta migração single-company (app webapp-nfs-repasse-medico), além do escopo nfse_repasse:write, as operações de escrita (POST /, PUT, DELETE e POST .../historico) exigem que o usuário autenticado tenha perfil administrador. A leitura (GET) é liberada para qualquer usuário autenticado (get_current_user).

Endpoints

POST /nfse-repasse/demonstrativos/

Cria um novo demonstrativo de repasse vinculado a um repasse de NFS-e médica. O campo id_usuario_created não entra no body - é forçado pelo service a partir do usuário autenticado.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (DemonstrativoRepasseCreate): id_repasse (obrigatório), id_medico, tipo_demonstrativo (default individual_medico), arquivo_url, campos de envio e uuid_legado (opcional, só para correlação de migração).

Request

{
  "id_repasse": 1,
  "id_medico": 1,
  "tipo_demonstrativo": "individual_medico",
  "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf"
}

Response 201 Created

{
  "success": true,
  "message": "Demonstrativo criado com sucesso!",
  "data": {
    "id": 1,
    "id_repasse": 1,
    "id_medico": 1,
    "tipo_demonstrativo": "individual_medico",
    "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
    "status_envio": "nao_enviado",
    "enviado_em": null,
    "id_usuario_enviou": null,
    "tentativas_envio": 0,
    "erro_envio": null,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-07-03T12:00:00Z",
    "updated_at": "2026-07-03T12:00:00Z"
  }
}

Escopo: nfse_repasse:write (+ perfil administrador)

GET /nfse-repasse/demonstrativos/by-repasse/{repasse_id}

Lista os demonstrativos de um repasse específico. Declarada antes da rota dinâmica /{demonstrativo_id} para não ser interpretada como um ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
repasse_idintpathsimID do repasse (repasse.id).
status_enviostrquerynãoFiltro por status de envio: nao_enviado, enviado ou erro.
pageintquerynãoPágina 1-based (≥1, opcional).
per_pageintquerynãoItens por página (1-500, opcional).

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Demonstrativos recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_repasse": 1,
      "id_medico": 1,
      "tipo_demonstrativo": "individual_medico",
      "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
      "status_envio": "nao_enviado",
      "enviado_em": null,
      "id_usuario_enviou": null,
      "tentativas_envio": 0,
      "erro_envio": null,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-07-03T12:00:00Z",
      "updated_at": "2026-07-03T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

GET /nfse-repasse/demonstrativos/

Lista todos os demonstrativos, com filtros opcionais combináveis. Lista vazia é um estado válido - retorna 200 OK com data: [].

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
id_repasseintquerynãoFiltro por repasse (repasse.id).
id_medicointquerynãoFiltro por médico (medico.id).
status_enviostrquerynãoFiltro por status de envio: nao_enviado, enviado ou erro.
pageintquerynãoPágina 1-based (≥1, opcional).
per_pageintquerynãoItens por página (1-500, opcional).

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Demonstrativos recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_repasse": 1,
      "id_medico": 1,
      "tipo_demonstrativo": "individual_medico",
      "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
      "status_envio": "nao_enviado",
      "enviado_em": null,
      "id_usuario_enviou": null,
      "tentativas_envio": 0,
      "erro_envio": null,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-07-03T12:00:00Z",
      "updated_at": "2026-07-03T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

POST /nfse-repasse/demonstrativos/{demonstrativo_id}/historico

Registra um evento no histórico do demonstrativo (sub-recurso aninhado, declarado antes de /{demonstrativo_id}). O id_demonstrativo_repasse vem do path, não do body.

Histórico é apenas persistência

Este endpoint apenas registra/persiste eventos (ex.: gerado, enviado, reenviado). O envio de e-mail em si é fase posterior, executada por um worker Celery - não há endpoint de envio aqui.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
demonstrativo_idintpathsimID do demonstrativo-pai.

O corpo da requisição segue o schema DemonstrativoHistoricoCreate, cujo único campo obrigatório é evento.

Request

{
  "evento": "enviado",
  "destinatario_email": "medico@exemplo.com.br",
  "status": "sucesso"
}

Response 201 Created

{
  "success": true,
  "message": "Evento registrado com sucesso!",
  "data": {
    "id": 1,
    "id_demonstrativo_repasse": 1,
    "evento": "enviado",
    "id_medico": 1,
    "id_usuario": 42,
    "tipo_demonstrativo": "individual_medico",
    "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
    "destinatario_email": "medico@exemplo.com.br",
    "status": "sucesso",
    "erro_mensagem": null,
    "observacao": null,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-07-03T12:05:00Z"
  }
}

Escopo: nfse_repasse:write (+ perfil administrador)

GET /nfse-repasse/demonstrativos/{demonstrativo_id}/historico

Lista o histórico de eventos de um demonstrativo (append-only, ordenado por criação). Demonstrativo inexistente retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
demonstrativo_idintpathsimID do demonstrativo-pai.
pageintquerynãoPágina 1-based (≥1, opcional).
per_pageintquerynãoItens por página (1-500).

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Histórico recuperado com sucesso!",
  "data": [
    {
      "id": 1,
      "id_demonstrativo_repasse": 1,
      "evento": "enviado",
      "id_medico": 1,
      "id_usuario": 42,
      "tipo_demonstrativo": "individual_medico",
      "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
      "destinatario_email": "medico@exemplo.com.br",
      "status": "sucesso",
      "erro_mensagem": null,
      "observacao": null,
      "uuid_legado": null,
      "id_usuario_created": 42,
      "created_at": "2026-07-03T12:05:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

GET /nfse-repasse/demonstrativos/{demonstrativo_id}

Detalha um demonstrativo pelo ID inteiro. Quando não encontrado, retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
demonstrativo_idintpathsimID interno do demonstrativo.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Demonstrativo encontrado!",
  "data": {
    "id": 1,
    "id_repasse": 1,
    "id_medico": 1,
    "tipo_demonstrativo": "individual_medico",
    "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
    "status_envio": "nao_enviado",
    "enviado_em": null,
    "id_usuario_enviou": null,
    "tentativas_envio": 0,
    "erro_envio": null,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-07-03T12:00:00Z",
    "updated_at": "2026-07-03T12:00:00Z"
  }
}

Escopo: nfse_repasse:read

PUT /nfse-repasse/demonstrativos/{demonstrativo_id}

Atualiza um demonstrativo existente. O body é um DemonstrativoRepasseUpdate com campos parciais - somente o que vier preenchido é alterado. Os campos id, uuid_legado, id_usuario_created, id_repasse, created_at e updated_at são imutáveis (não aparecem no Update, o que bloqueia mass-assignment e transferência de repasse).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
demonstrativo_idintpathsimID do demonstrativo.

Request (DemonstrativoRepasseUpdate - todos os campos opcionais)

{
  "status_envio": "enviado",
  "enviado_em": "2026-07-03T12:10:00Z",
  "id_usuario_enviou": 42
}

Response 200 OK

{
  "success": true,
  "message": "Demonstrativo atualizado!",
  "data": {
    "id": 1,
    "id_repasse": 1,
    "id_medico": 1,
    "tipo_demonstrativo": "individual_medico",
    "arquivo_url": "https://.../demonstrativos/1/individual/1/demo.pdf",
    "status_envio": "enviado",
    "enviado_em": "2026-07-03T12:10:00Z",
    "id_usuario_enviou": 42,
    "tentativas_envio": 1,
    "erro_envio": null,
    "uuid_legado": null,
    "id_usuario_created": 42,
    "created_at": "2026-07-03T12:00:00Z",
    "updated_at": "2026-07-03T12:10:00Z"
  }
}

Escopo: nfse_repasse:write (+ perfil administrador)

DELETE /nfse-repasse/demonstrativos/{demonstrativo_id}

Remove um demonstrativo pelo ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
demonstrativo_idintpathsimID do demonstrativo.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Demonstrativo removido!",
  "data": null
}

Escopo: nfse_repasse:write (+ perfil administrador)

Schemas

DemonstrativoRepasseCreate

id_usuario_created não entra no Create (forçado pelo service). uuid_legado é aceito apenas para correlação na migração e torna-se imutável depois.

CampoTipoObrigatórioObservações
id_repasseintsimID do repasse (repasse.id).
id_medicointnãoID do médico. Opcional (consolidado não tem médico).
tipo_demonstrativostrnãoindividual_medico (default) ou consolidado. Máx. 40 caracteres.
arquivo_urlstrnãoURL pública do PDF gerado.
status_enviostrnãonao_enviado (default), enviado ou erro. Máx. 30.
enviado_emdatetimenãoTimestamp do envio (preenchido pelo worker Celery - fase posterior).
id_usuario_enviouintnãoID do usuário que disparou o envio (auditoria).
tentativas_enviointnãoContador de tentativas (≥0, default 0).
erro_enviostrnãoMensagem do último erro de envio.
uuid_legadostrnãoUUID do registro original no Supabase (máx. 36). Imutável depois.

DemonstrativoRepasseUpdate

Todos os campos são opcionais; apenas os enviados são atualizados. Campos de identidade/origem são imutáveis e não aparecem aqui.

CampoTipoObservações
id_medicointReatribui o médico do demonstrativo.
tipo_demonstrativostrMáx. 40 caracteres.
arquivo_urlstrNova URL do PDF.
status_enviostrnao_enviado, enviado ou erro. Máx. 30.
enviado_emdatetimeTimestamp do envio.
id_usuario_enviouintUsuário que disparou o envio.
tentativas_enviointContador de tentativas (≥0).
erro_enviostrMensagem do último erro.

DemonstrativoRepasseRead

Estende o Base com os campos gerenciados (ID, owner, timestamps).

CampoTipoNotas
idintID interno do demonstrativo.
id_repasseintID do repasse.
id_medicointID do médico (nulo em consolidado).
tipo_demonstrativostrindividual_medico ou consolidado.
arquivo_urlstrURL pública do PDF.
status_enviostrnao_enviado, enviado ou erro.
enviado_emdatetimeTimestamp do envio (ou null).
id_usuario_enviouintUsuário que disparou o envio.
tentativas_enviointContador de tentativas.
erro_enviostrMensagem do último erro.
uuid_legadostrUUID de origem (migração).
id_usuario_createdintUsuário que criou (auditoria).
created_atdatetimeTimestamp de criação (server-side).
updated_atdatetimeTimestamp da última atualização.

DemonstrativoHistoricoCreate

Evento de histórico do demonstrativo. id_demonstrativo_repasse vem do path da rota; id_usuario_created é forçado pelo service.

CampoTipoObrigatórioObservações
eventostrsimEvento registrado (gerado, enviado, reenviado, …). Máx. 40.
id_medicointnãoID do médico.
id_usuariointnãoID do usuário que disparou o evento.
tipo_demonstrativostrnãoindividual_medico (default) ou consolidado. Máx. 40.
arquivo_urlstrnãoURL do PDF no evento.
destinatario_emailstrnãoE-mail destinatário (quando o evento é um envio). Máx. 150.
statusstrnãosucesso (default) ou erro. Máx. 20.
erro_mensagemstrnãoMensagem de erro do evento.
observacaostrnãoObservação livre.
uuid_legadostrnãoUUID de origem (migração). Máx. 36.

O schema de leitura correspondente, DemonstrativoHistoricoRead, adiciona id, id_demonstrativo_repasse, id_usuario_created, uuid_legado e created_at (append-only - sem updated_at).

Notas

  • Toda resposta segue o envelope { success, message, data } (ou { success: false, message, details } em erro).
  • As rotas com path estático (/by-repasse/{repasse_id}) e os sub-recursos aninhados (/{demonstrativo_id}/historico) são declarados antes da rota dinâmica /{demonstrativo_id}. O FastAPI faz match na ordem de declaração - inverter quebraria os endpoints estáticos.
  • arquivo_url referencia o PDF do demonstrativo. A geração e o envio de e-mail são fases posteriores executadas por worker Celery; este domínio apenas persiste os campos de status de envio e registra eventos no histórico.
  • Erros de negócio são mapeados por mensagem canônica do service: "não encontrado" → 404, "já existe" → 409, "acesso negado" → 403, demais → 422.