openapi: 3.0.3
info:
  title: eGestor API
  version: 1.0.0
  description: |
    O [eGestor](https://www.egestor.com.br) disponibiliza uma API REST que permite o acesso
    aos contatos, produtos, vendas e demais módulos do sistema.

    ## Autenticação
    A API utiliza **OAuth2** como forma de autenticação. O `access_token` possui validade de
    **15 minutos**. Utilize o `refresh_token` para solicitar um novo sem pedir login novamente.

    ## Rate Limiting
    Limite de **60 requisições por minuto** por aplicação+usuário.
    Os headers `X-RateLimit-Limit` e `X-RateLimit-Remaining` são retornados em todas as respostas.

    ## Paginação
    As listagens retornam **50 registros por página**. Use o parâmetro `page` para navegar.

    ## Códigos de resposta
    | Código | Descrição |
    |--------|-----------|
    | `200`  | Sucesso |
    | `400`  | Dados inválidos ou campos ausentes |
    | `401`  | Token de acesso inválido |
    | `404`  | Registro não encontrado |
    | `410`  | Registro foi excluído do sistema |
    | `422`  | Dados fora do escopo definido |
    | `429`  | Limite de requisições atingido |

    ## Contato
    Dúvidas e solicitações: kasper@zipline.com.br

servers:
  - url: https://v4.egestor.com.br/api
    description: Produção

security:
  - bearerAuth: []

# =============================================================================
# COMPONENTS
# =============================================================================
components:

  securitySchemes:
    bearerAuth:
      type: apiKey
      in: header
      name: Authorization
      description: "Token Bearer obtido via `/oauth/access_token`. Formato: `Bearer <access_token>`"

  parameters:
    pathCodigo:
      name: codigo
      in: path
      required: true
      schema:
        type: integer
    queryFiltro:
      name: filtro
      in: query
      schema:
        type: string
      description: Busca textual nos campos principais do recurso
    queryPage:
      name: page
      in: query
      schema:
        type: integer
        default: 1
    queryOrderBy:
      name: orderBy
      in: query
      schema:
        type: string
      description: "Ordenação: `campo,asc` ou `campo,desc`"
    queryUpdatedAfter:
      name: updatedAfter
      in: query
      schema:
        type: string
        format: date-time
      description: Filtra registros atualizados após esta data (requer ativação pelo suporte)
    queryUpdatedBefore:
      name: updatedBefore
      in: query
      schema:
        type: string
        format: date-time
      description: Filtra registros atualizados antes desta data (requer ativação pelo suporte)
    queryFields:
      name: fields
      in: query
      schema:
        type: string
      description: Campos a retornar, separados por vírgula
    queryDtIni:
      name: dtIni
      in: query
      schema:
        type: string
        format: date
      description: Data inicial (YYYY-MM-DD)
    queryDtFim:
      name: dtFim
      in: query
      schema:
        type: string
        format: date
      description: Data final (YYYY-MM-DD)

  schemas:

    # ── Paginação ──────────────────────────────────────────────
    PaginatedMeta:
      type: object
      properties:
        total:        { type: integer, example: 100 }
        per_page:     { type: integer, example: 50 }
        current_page: { type: integer, example: 1 }
        last_page:    { type: integer, example: 2 }
        next_page_url:
          type: string
          nullable: true
          example: "https://api.egestor.com.br/v1/contatos?page=2"
        prev_page_url:
          type: string
          nullable: true
          example: null
        from: { type: integer, example: 1 }
        to:   { type: integer, example: 50 }

    # ── Respostas genéricas ───────────────────────────────────
    ErrorResponse:
      type: object
      properties:
        errCode:   { type: integer, example: 404 }
        errMsg:    { type: string,  example: "Nenhum registro com código 1 encontrado" }
        errObs:    { type: string,  nullable: true }
        errFields: { type: object,  nullable: true }

    SuccessResponse:
      type: object
      properties:
        code:   { type: integer, example: 200 }
        msg:    { type: string,  example: "Operação realizada com sucesso." }
        obs:    { type: string,  nullable: true }
        fields: { type: object,  nullable: true }

    # ── Contatos ──────────────────────────────────────────────
    ContatoSummary:
      type: object
      properties:
        codigo:  { type: integer, readOnly: true, example: 1 }
        tipo:
          type: array
          items: { type: string, enum: [cliente, fornecedor, transportadora] }
          example: ["cliente"]
        nome:    { type: string, example: "Empresa Exemplo LTDA" }
        emails:  { type: array, items: { type: string } }
        fones:   { type: array, items: { type: string } }
        tags:    { type: array, items: { type: string } }
        cidade:  { type: string }
        uf:      { type: string, example: "SP" }

    ContatoDetalhado:
      type: object
      properties:
        codigo:             { type: integer, readOnly: true }
        tipo:
          type: array
          items: { type: string, enum: [cliente, fornecedor, transportadora] }
        nome:               { type: string, maxLength: 60 }
        fantasia:           { type: string, maxLength: 60 }
        nomeContato:        { type: string, maxLength: 60 }
        cpfcnpj:            { type: string, example: "83294489654" }
        clienteFinal:       { type: boolean }
        indicadorIE:
          type: integer
          enum: [1, 2, 9]
          description: "1=Contribuinte ICMS, 2=Isento de IE, 9=Não contribuinte"
        inscricaoEstadual:  { type: string }
        inscricaoEstadualST: { type: string }
        inscricaoMunicipal: { type: string }
        suframa:            { type: string }
        emails:  { type: array, items: { type: string } }
        fones:   { type: array, items: { type: string } }
        logradouro:  { type: string }
        numero:      { type: string }
        complemento: { type: string }
        bairro:      { type: string }
        cidade:      { type: string }
        codIBGE:     { type: string, example: "355030" }
        uf:          { type: string, example: "SP" }
        cep:         { type: string }
        pais:        { type: string }
        tags:    { type: array, items: { type: string } }
        obs:     { type: string }
        dtNasc:  { type: string, format: date,      example: "1990-05-12" }
        dtCad:   { type: string, format: date-time, readOnly: true }

    ContatoInput:
      type: object
      required: [nome, tipo]
      properties:
        nome:           { type: string, maxLength: 60 }
        fantasia:       { type: string, maxLength: 60 }
        nomeParaContato: { type: string }
        tipo:
          type: array
          items: { type: string, enum: [cliente, fornecedor, transportadora] }
        cpfcnpj:        { type: string }
        dtNasc:         { type: string, format: date }
        emails:         { type: array, items: { type: string } }
        fones:          { type: array, items: { type: string } }
        cep:            { type: integer }
        logradouro:     { type: string }
        numero:         { type: string }
        complemento:    { type: string }
        bairro:         { type: string }
        cidade:         { type: string }
        codIBGE:        { type: string }
        uf:             { type: string, minLength: 2, maxLength: 2 }
        pais:           { type: string }
        clienteFinal:   { type: boolean }
        indicadorIE:    { type: integer, enum: [1, 2, 9] }
        inscricaoMunicipal:  { type: string }
        inscricaoEstadual:   { type: string }
        inscricaoEstadualST: { type: string }
        suframa:        { type: string }
        obs:            { type: string }
        tags:           { type: array, items: { type: string } }

    # ── Produtos ──────────────────────────────────────────────
    ProdutoBase:
      type: object
      properties:
        codigo:           { type: integer, readOnly: true }
        descricao:        { type: string }
        codigoProprio:    { type: string }
        estoque:          { type: number }
        estoqueMinimo:    { type: number }
        controlarEstoque: { type: boolean }
        margemLucro:      { type: number }
        precoCusto:       { type: number }
        precoVenda:       { type: number }
        origemFiscal:
          type: integer
          enum: [0,1,2,3,4,5,6,7,8]
          description: "0=Nacional, 1..8=Estrangeira (ver tabela ICMS)"
        unidadeTributada: { type: string, example: "UN" }
        refEanGtin:       { type: string }
        ncm:              { type: string }
        excecaoIPI:       { type: integer }
        codigoCEST:       { type: string }
        pesoBruto:        { type: number }
        pesoLiquido:      { type: number }
        codigoGrupoTributos: { type: integer }
        anotacoesNFE:     { type: string }
        anotacoesInternas: { type: string }
        tags:    { type: array, items: { type: string } }
        dtCad:   { type: string, format: date-time, readOnly: true }

    ProdutoInput:
      allOf:
        - $ref: '#/components/schemas/ProdutoBase'
        - type: object
          required: [descricao]

    # ── Serviços ──────────────────────────────────────────────
    ServicoBase:
      type: object
      properties:
        codigo:              { type: integer, readOnly: true }
        descricao:           { type: string }
        precoVenda:          { type: number }
        codigoGrupoTributos: { type: integer }
        itemListaServico:    { type: string, description: "Código LC116" }
        cnae:                { type: string }
        UcListServ:          { type: string }
        tags:    { type: array, items: { type: string } }
        dtCad:   { type: string, format: date-time, readOnly: true }

    ServicoInput:
      allOf:
        - $ref: '#/components/schemas/ServicoBase'
        - type: object
          required: [descricao]

    # ── Vendas ────────────────────────────────────────────────
    VendaSummary:
      type: object
      properties:
        codigo:      { type: integer, readOnly: true }
        codContato:  { type: integer }
        nomeContato: { type: string }
        codVendedor: { type: integer }
        dtVenda:     { type: string, format: date }
        dtEntrega:   { type: string, format: date, nullable: true }
        valorTotal:  { type: number }
        valorFinanc: { type: number }
        codsNFe:     { type: array, items: { type: integer } }
        situacao:
          type: integer
          enum: [10, 50, 60, 80]
          description: "10=Orçamento, 50=Venda, 60=Cancelada, 80=Finalizada"
        tags:        { type: array, items: { type: string } }

    VendaProdutoItem:
      type: object
      properties:
        codigo:       { type: integer, readOnly: true }
        codProduto:   { type: integer }
        tipoProd:     { type: string, enum: [produto, servico] }
        descricao:    { type: string }
        codigoProprio: { type: string }
        preco:        { type: number }
        custo:        { type: number }
        custoCadProd: { type: number }
        quant:        { type: number }
        vDesc:        { type: number }
        valorIPI:     { type: number }
        valorST:      { type: number }
        obs:          { type: string }

    VendaFinanceiroItem:
      type: object
      properties:
        codigo:      { type: integer, readOnly: true }
        codFormaPgto: { type: integer }
        codCaixa:    { type: integer }
        codRecibo:   { type: integer }
        situacao:    { type: integer }
        dtComp:      { type: string, format: date }
        dtVenc:      { type: string, format: date }
        valor:       { type: number }

    VendaDetalhada:
      allOf:
        - $ref: '#/components/schemas/VendaSummary'
        - type: object
          properties:
            nomeVendedor:  { type: string }
            dtCad:         { type: string, format: date-time, readOnly: true }
            valorEntrada:  { type: number }
            numParcelas:   { type: integer }
            clienteFinal:  { type: boolean }
            customizado:
              type: object
              additionalProperties: { type: string }
              description: "Campos personalizados xCampo1..xCampo4"
              example: { xCampo1: "Observações gerais" }
            produtos:
              type: array
              items: { $ref: '#/components/schemas/VendaProdutoItem' }
            financeiros:
              type: array
              items: { $ref: '#/components/schemas/VendaFinanceiroItem' }

    VendaInput:
      type: object
      properties:
        codContato:  { type: integer }
        dtVenda:     { type: string, format: date, example: "2024-01-15" }
        dtEntrega:   { type: string, format: date }
        codVendedor: { type: integer }
        situacao:
          type: integer
          enum: [10, 50]
          description: "10=Orçamento, 50=Venda (padrão: 50)"
          default: 50
        customizado:
          type: object
          additionalProperties: { type: string }
          description: "Campos personalizados xCampo1..xCampo4"
        tags:        { type: array, items: { type: string } }
        produtos:
          type: array
          items:
            type: object
            required: [codProduto, quant, preco]
            properties:
              codProduto: { type: integer }
              quant:      { type: number }
              preco:      { type: number }
              vDesc:      { type: number, default: 0 }
              obs:        { type: string }
        financeiros:
          type: array
          items:
            type: object
            properties:
              dtVenc:      { type: string, format: date }
              valor:       { type: number }
              codFormaPgto: { type: integer }
              codCaixa:    { type: integer }
        despesas:
          type: array
          items:
            type: object
            description: Pagamentos/despesas associados à venda

    VendaUpdateInput:
      type: object
      properties:
        situacao:
          type: integer
          enum: [10, 50, 60, 80]
          description: "10=Orçamento, 50=Venda, 60=Cancelada, 80=Finalizada"
        situacaoOS:      { type: integer }
        campoAdicional1: { type: string }
        campoAdicional2: { type: string }
        campoAdicional3: { type: string }
        campoAdicional4: { type: string }

    # ── Financeiro (Recebimentos / Pagamentos) ────────────────
    FinanceiroSummary:
      type: object
      properties:
        codigo:        { type: integer, readOnly: true }
        codRecibo:     { type: integer }
        descricao:     { type: string }
        previsao:      { type: integer }
        situacao:      { type: integer }
        codDisponivel: { type: integer }
        codBoleto:     { type: integer }
        data:          { type: string, format: date }
        valor:         { type: number }
        contato:       { type: string }
        nomeDisponivel: { type: string }

    FinanceiroDetalhado:
      type: object
      properties:
        codigo:        { type: integer, readOnly: true }
        codPlanoContas: { type: integer }
        codFormaPgto:  { type: integer }
        numDoc:        { type: string }
        descricao:     { type: string }
        valor:         { type: number }
        dtVenc:        { type: string, format: date }
        dtPgto:        { type: string, format: date, nullable: true }
        dtCad:         { type: string, format: date-time, readOnly: true }
        situacao:      { type: integer }
        codContato:    { type: integer }
        origem:        { type: string, example: "vendas" }
        obs:           { type: string }
        tags:          { type: array, items: { type: string } }

    FinanceiroInput:
      type: object
      required: [descricao, valor, dtVenc]
      properties:
        codPlanoContas: { type: integer }
        codFormaPgto:  { type: integer }
        numDoc:        { type: string }
        descricao:     { type: string }
        valor:         { type: number }
        dtVenc:        { type: string, format: date }
        dtPgto:        { type: string, format: date }
        dtComp:        { type: string, format: date }
        codContato:    { type: integer }
        recebido:
          type: boolean
          description: Se true, cria o lançamento já como recebido/pago
        obs:           { type: string }
        tags:          { type: array, items: { type: string } }

    EfetivarInput:
      type: object
      properties:
        valor:  { type: number,  description: "Valor efetivamente recebido/pago" }
        dtPgto: { type: string, format: date, description: "Data do pagamento" }
        dtComp: { type: string, format: date, description: "Data de competência" }
        dtCred: { type: string, format: date, description: "Data de crédito" }

    # ── Boletos ───────────────────────────────────────────────
    BoletoSummary:
      type: object
      properties:
        codigo:         { type: integer, readOnly: true }
        contatoNome:    { type: string }
        dtVenc:         { type: string, format: date }
        dtEmissao:      { type: string, format: date }
        valor:          { type: number }
        valorPago:      { type: number }
        codRecebimentos: { type: array, items: { type: integer } }
        situacao:
          type: integer
          enum: [10, 50]
          description: "10=Em aberto, 50=Recebido"

    BoletoDetalhado:
      type: object
      properties:
        codigo:         { type: integer, readOnly: true }
        codContato:     { type: integer }
        contatoNome:    { type: string }
        contatoEmail:   { type: string }
        dtVenc:         { type: string, format: date }
        dtEmissao:      { type: string, format: date }
        valor:          { type: number }
        valorPago:      { type: number }
        codRecebimentos: { type: array, items: { type: integer } }
        link:           { type: string, description: "URL para o PDF do boleto" }

    BoletoInput:
      type: object
      required: [codContato, dtVenc, codRecebimentos]
      properties:
        codContato:
          type: integer
          description: Código do contato (devedor)
        dtVenc:
          type: string
          format: date
          description: Data de vencimento
        codRecebimentos:
          type: array
          items: { type: integer }
          description: Códigos dos recebimentos a incluir no boleto

    # ── Compras ───────────────────────────────────────────────
    CompraSummary:
      type: object
      properties:
        codigo:     { type: integer, readOnly: true }
        codContato: { type: integer }
        nomeContato: { type: string }
        numNota:    { type: string }
        dtCompra:   { type: string, format: date }
        valorTotal: { type: number }
        obs:        { type: string }
        tags:       { type: array, items: { type: string } }
        situacao:
          type: integer
          enum: [10, 50]
          description: "10=Orçamento, 50=Compra efetivada"
        ativo: { type: integer }

    CompraProdutoItem:
      type: object
      properties:
        codigo:       { type: integer, readOnly: true }
        codProduto:   { type: integer }
        descricao:    { type: string }
        codigoProprio: { type: string }
        custoBruto:   { type: number }
        quant:        { type: number }
        vDesc:        { type: number }
        valorIPI:     { type: number }
        valorST:      { type: number }

    CompraFinanceiroItem:
      type: object
      properties:
        codigo:    { type: integer, readOnly: true }
        codBoleto: { type: integer }
        descricao: { type: string }
        codCaixa:  { type: integer }
        codRecibo: { type: integer }
        situacao:  { type: integer }
        dtComp:    { type: string, format: date }
        dtVenc:    { type: string, format: date }
        tags:      { type: array, items: { type: string } }
        valor:     { type: number }

    CompraDetalhada:
      allOf:
        - $ref: '#/components/schemas/CompraSummary'
        - type: object
          properties:
            codCompraXML: { type: integer }
            dtCad:        { type: string, format: date-time, readOnly: true }
            extFrete:     { type: number }
            extST:        { type: number }
            extDesp:      { type: number }
            produtos:
              type: array
              items: { $ref: '#/components/schemas/CompraProdutoItem' }
            financeiros:
              type: array
              items: { $ref: '#/components/schemas/CompraFinanceiroItem' }

    CompraInput:
      type: object
      required: [codContato, dtCompra, situacao]
      properties:
        codContato: { type: integer }
        numNota:    { type: string }
        dtCompra:   { type: string, format: date }
        extFrete:   { type: number, default: 0 }
        extST:      { type: number, default: 0 }
        extDesp:    { type: number, default: 0 }
        obs:        { type: string }
        tags:       { type: array, items: { type: string } }
        situacao:
          type: integer
          enum: [10, 50]
          description: "10=Orçamento, 50=Compra"
        atualizarPrecoVenda:
          type: boolean
          default: false
          description: Atualiza o preço de venda dos produtos com base no custo + margem
        produtos:
          type: array
          items:
            type: object
            required: [codProduto, custoBruto, quant]
            properties:
              codProduto: { type: integer }
              custoBruto: { type: number }
              quant:      { type: number }
              vDesc:      { type: number, default: 0 }
              valorIPI:   { type: number, default: 0 }
              valorST:    { type: number, default: 0 }
        financeiros:
          type: array
          items:
            type: object
            properties:
              descricao:  { type: string }
              codCaixa:   { type: integer }
              codRecibo:  { type: integer }
              dtComp:     { type: string, format: date }
              dtVenc:     { type: string, format: date }
              tags:       { type: array, items: { type: string } }
              valor:      { type: number }

    # ── Usuários ──────────────────────────────────────────────
    UsuarioSummary:
      type: object
      properties:
        codigo:        { type: integer, readOnly: true }
        nome:          { type: string }
        login:         { type: string }
        email:         { type: string }
        tags:          { type: array, items: { type: string } }
        usuarioSistema: { type: boolean }
        vendedor:      { type: boolean }
        comisProd:     { type: number }
        comisServ:     { type: number }
        comisFin:      { type: number }

    UsuarioDetalhado:
      allOf:
        - $ref: '#/components/schemas/UsuarioSummary'
        - type: object
          properties:
            permissoes:
              type: array
              items: { type: string }
              description: Lista de permissões do usuário

    # ── Categorias ────────────────────────────────────────────
    Categoria:
      type: object
      properties:
        codigo:   { type: integer, readOnly: true }
        descricao: { type: string }
        ativo:    { type: integer }

    CategoriaInput:
      type: object
      required: [descricao]
      properties:
        descricao: { type: string }

    # ── Disponíveis (Contas/Caixas) ───────────────────────────
    Disponivel:
      type: object
      properties:
        codigo: { type: integer, readOnly: true }
        nome:   { type: string }
        saldo:  { type: number }
        visivel: { type: boolean }

    DisponiveiInput:
      type: object
      required: [nome]
      properties:
        nome:  { type: string }
        saldo: { type: number, default: 0 }

    # ── Plano de Contas ───────────────────────────────────────
    PlanoConta:
      type: object
      properties:
        codigo:   { type: integer, readOnly: true }
        nome:     { type: string }
        codPai:   { type: integer, nullable: true }
        debitar:  { type: boolean }
        creditar: { type: boolean }

    PlanoContaInput:
      type: object
      required: [nome]
      properties:
        nome:     { type: string }
        codPai:   { type: integer }
        debitar:  { type: boolean }
        creditar: { type: boolean }

    # ── Formas de Pagamento ───────────────────────────────────
    FormaPagamento:
      type: object
      properties:
        codigo:       { type: integer, readOnly: true }
        nome:         { type: string }
        codDisponivel: { type: integer }
        tipoCusto:    { type: integer }
        valorCusto:   { type: number }
        formaPgtoNFCe: { type: integer }

    FormaPagamentoInput:
      type: object
      required: [nome]
      properties:
        nome:         { type: string }
        codDisponivel: { type: integer }
        tipoCusto:    { type: integer }
        valorCusto:   { type: number }
        formaPgtoNFCe: { type: integer }

    # ── Ajuste de Estoque ─────────────────────────────────────
    AjusteEstoque:
      type: object
      properties:
        codigo:      { type: integer, readOnly: true }
        codContato:  { type: integer }
        nomeContato: { type: string }
        motivo:
          type: integer
          enum: [10, 20, 30, 40, 50]
          description: "10=Ajuste geral, 20=Envio RMA, 30=Retorno RMA, 40=Devolução venda, 50=Devolução compra"
        dtCad: { type: string, format: date-time, readOnly: true }
        obs:   { type: string }
        tags:  { type: array, items: { type: string } }
        produtos:
          type: array
          items:
            type: object
            properties:
              codProduto:   { type: integer }
              nomeProduto:  { type: string }
              codProprio:   { type: string }
              estoqueFinal: { type: number }
              quantidade:   { type: number, description: "Variação aplicada (pode ser negativa)" }

    AjusteEstoqueInput:
      type: object
      required: [motivo, produtos]
      properties:
        motivo:
          type: integer
          enum: [10, 20, 30, 40, 50]
          description: "10=Ajuste geral, 20=Envio RMA, 30=Retorno RMA, 40=Devolução venda, 50=Devolução compra"
        codContato: { type: integer, description: "Código do responsável pelo ajuste" }
        obs:        { type: string }
        tags:       { type: array, items: { type: string } }
        produtos:
          type: array
          items:
            type: object
            required: [codProduto, estoqueFinal]
            properties:
              codProduto:   { type: integer }
              estoqueFinal: { type: number }

    # ── Disco Virtual ─────────────────────────────────────────
    Arquivo:
      type: object
      properties:
        codigo: { type: integer, readOnly: true }
        nome:   { type: string }
        tamanho: { type: integer }
        dtCad:  { type: string, format: date-time, readOnly: true }
        link:   { type: string, description: "URL para download" }

    # ── Config. Tributária ────────────────────────────────────
    ConfigTrib:
      type: object
      properties:
        codigo:   { type: integer, readOnly: true }
        descricao: { type: string }

    # ── NFSe ──────────────────────────────────────────────────
    NFSe:
      type: object
      properties:
        codigo:     { type: integer, readOnly: true }
        numeroRPS:  { type: string }
        numeroNota: { type: string }
        dtEmissao:  { type: string, format: date }
        situacao:   { type: integer }
        valorTotal: { type: number }
        codVenda:   { type: integer }

    NFSeInput:
      type: object
      required: [itens]
      properties:
        numeroRPS:        { type: string }
        itens:            { type: array, items: { type: object } }
        enviarPrefeitura: { type: boolean, default: true }

    # ── NFe ───────────────────────────────────────────────────
    NFe:
      type: object
      properties:
        codigo:  { type: integer, readOnly: true }
        chNFe:   { type: string, description: "Chave de acesso (44 dígitos)" }
        nNF:     { type: string, description: "Número da nota fiscal" }
        dhEmi:   { type: string, format: date-time }
        situacao: { type: integer }
        tags:    { type: array, items: { type: string } }

    # ── NF-e Recebida ────────────────────────────────────────
    NFeRecebida:
      type: object
      properties:
        codigo:      { type: integer, readOnly: true }
        tipo:        { type: string, enum: [nfe, nfeRec, evento], description: "Tipo do documento" }
        nsu:         { type: string, description: "Número Sequencial Único" }
        chave:       { type: string, description: "Chave de acesso (44 dígitos)" }
        cnpjEmit:    { type: string, description: "CNPJ do emitente" }
        cnpjDest:    { type: string, description: "CNPJ do destinatário" }
        nomeEmit:    { type: string, description: "Nome do emitente" }
        nomeDest:    { type: string }
        dtEmissao:   { type: string, format: date-time }
        dtCad:       { type: string, format: date-time, readOnly: true }
        valorTotal:  { type: number }
        ambiente:    { type: integer, enum: [1, 2], description: "1=Produção, 2=Homologação" }
        situacao:    { type: integer }
        manifesto:   { type: string, description: "Último tpEvento de manifesto registrado" }

    # ── PIX ───────────────────────────────────────────────────
    Pix:
      type: object
      properties:
        codigo:  { type: integer, readOnly: true }
        codFin:  { type: integer }
        valor:   { type: number }
        situacao: { type: integer }
        txid:    { type: string }
        qrCode:  { type: string }
        dtVenc:  { type: string, format: date }

    # ── Devoluções ────────────────────────────────────────────
    DevolucaoProdutoItem:
      type: object
      properties:
        codigo:          { type: integer, readOnly: true }
        quantDevolvida:  { type: number }
        valorDevolvido:  { type: number }
        descricao:       { type: string }
        codigoProprio:   { type: string }

    Devolucao:
      type: object
      properties:
        codigo:      { type: integer, readOnly: true }
        codVenda:    { type: integer }
        codContato:  { type: integer }
        nomeContato: { type: string }
        dtVenda:     { type: string, format: date }
        dtCad:       { type: string, format: date-time, readOnly: true }
        situOS:      { type: string }
        obs:         { type: string }
        tags:        { type: array, items: { type: string } }
        produtos:
          type: array
          items: { $ref: '#/components/schemas/DevolucaoProdutoItem' }

    DevolucaoInput:
      type: object
      required: [codVenda, produtos]
      description: >
        Ao realizar a devolução é possível: (1) editar o recebimento da venda via `finReceber`,
        (2) gerar um pagamento ao cliente via `finPagar`, ou (3) apenas ajustar o estoque
        sem alterações financeiras (omitindo ambos).
      properties:
        codVenda:
          type: integer
          description: Código da venda que será devolvida
        tags:  { type: array, items: { type: string } }
        obs:   { type: string, description: "Motivo da devolução" }
        produtos:
          type: array
          items:
            type: object
            required: [codigo, valorDevolvido, quantDevolvida]
            properties:
              codigo:         { type: integer, description: "Código do item na venda" }
              valorDevolvido: { type: number }
              quantDevolvida: { type: number }
        finReceber:
          type: array
          description: Edita recebimentos da venda (opcional)
          items:
            type: object
            properties:
              codigo: { type: integer, description: "Código do recebimento a editar" }
              valor:  { type: number }
        finPagar:
          type: array
          description: Gera pagamentos ao cliente (opcional)
          items:
            type: object
            properties:
              valor:         { type: number }
              data:          { type: string, format: date }
              codFormaPgto:  { type: integer }
              codDisponivel: { type: integer }
              codPlanoConta: { type: integer }
              situacao:      { type: integer }

    # ── Webhooks ──────────────────────────────────────────────
    Webhook:
      type: object
      properties:
        codigo: { type: integer, readOnly: true }
        url:    { type: string, format: uri }
        evento: { type: string }
        ativo:  { type: boolean }

    WebhookInput:
      type: object
      required: [url, evento]
      properties:
        url:    { type: string, format: uri }
        evento: { type: string }

  responses:
    Unauthorized:
      description: Token de acesso inválido ou ausente
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            errCode: 401
            errMsg: "Não foi possível acessar o sistema. Verifique seu \"access_token\"."
            errObs: "access_denied"
            errFields: null
    NotFound:
      description: Registro não encontrado
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            errCode: 404
            errMsg: "Nenhum registro com código 1 encontrado"
            errObs: null
            errFields: null
    Gone:
      description: Registro foi excluído do sistema
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            errCode: 410
            errMsg: "Registro com código 1 não existe."
            errObs: null
            errFields: null
    BadRequest:
      description: Dados inválidos ou campos obrigatórios ausentes
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    TooManyRequests:
      description: Limite de 60 requisições/minuto atingido
      headers:
        X-RateLimit-Limit:
          schema: { type: integer }
        X-RateLimit-Remaining:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }

# =============================================================================
# TAGS
# =============================================================================
tags:
  - name: Autenticação
    description: OAuth2 — obtenção e renovação de tokens
  - name: Contatos
    description: Clientes, fornecedores e transportadoras
  - name: Produtos
    description: Produtos para venda e controle de estoque
  - name: Serviços
    description: Serviços para venda
  - name: Vendas
    description: Pedidos de venda e orçamentos
  - name: Recebimentos
    description: Contas a receber
  - name: Pagamentos
    description: Contas a pagar
  - name: Boletos
    description: Boletos bancários
  - name: Compras
    description: Pedidos de compra
  - name: NFSe
    description: Notas Fiscais de Serviço Eletrônicas
  - name: NFe
    description: Notas Fiscais Eletrônicas (produto)
  - name: PIX
    description: Cobranças via PIX
  - name: Devoluções
    description: Devoluções de vendas
  - name: Categorias
    description: Categorias de produtos/serviços
  - name: Usuários
    description: Usuários do sistema
  - name: Disponíveis
    description: Contas e caixas financeiros
  - name: Plano de Contas
    description: Plano de contas contábil
  - name: Formas de Pagamento
    description: Formas de pagamento cadastradas
  - name: Ajuste de Estoque
    description: Ajustes manuais de estoque
  - name: Disco Virtual
    description: Armazenamento de arquivos
  - name: Config. Tributária
    description: Grupos de tributação (configTrib)
  - name: Webhooks
    description: Notificações de eventos
  - name: Relatórios
    description: Relatórios gerenciais
  - name: Empresa
    description: Dados cadastrais da empresa

# =============================================================================
# PATHS
# =============================================================================
paths:

  # ── OAuth ──────────────────────────────────────────────────
  /oauth/access_token:
    post:
      tags: [Autenticação]
      summary: Obter access_token
      description: |
        Utiliza o `personal_token` JWT para obter um `access_token`.
        O `access_token` tem validade de **15 minutos**.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [grant_type, personal_token]
              properties:
                grant_type:
                  type: string
                  enum: [personal]
                personal_token: { type: string, description: "JWT obtido nas configurações da conta" }
            examples:
              personal_token:
                summary: Com personal_token
                value:
                  grant_type: personal
                  personal_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      x-postman:
        event:
          - listen: test
            script:
              type: text/javascript
              exec:
                - "var res = pm.response.json();"
                - "if (res.access_token) {"
                - "  pm.collectionVariables.set('bearerAuth', 'Bearer ' + res.access_token);"
                - "  console.log('✅ bearerAuth definido:', pm.collectionVariables.get('bearerAuth').substring(0, 30) + '...');"
                - "}"
      responses:
        '200':
          description: Token obtido com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:  { type: string }
                  token_type:    { type: string, example: "Bearer" }
                  expires_in:    { type: integer, example: 900, description: "Validade em segundos" }
                  refresh_token: { type: string }
                  personal_token: { type: string }

  # ── Contatos ───────────────────────────────────────────────
  /v1/contatos:
    get:
      tags: [Contatos]
      summary: Listar contatos
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryUpdatedAfter'
        - $ref: '#/components/parameters/queryUpdatedBefore'
        - $ref: '#/components/parameters/queryFields'
        - name: tipo
          in: query
          schema: { type: string, enum: [cliente, fornecedor, transportadora] }
      responses:
        '200':
          description: Lista paginada de contatos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/ContatoSummary' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

    post:
      tags: [Contatos]
      summary: Criar contato
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ContatoInput' }
            example:
              nome: "Empresa Exemplo LTDA"
              fantasia: "Exemplo"
              tipo: ["cliente"]
              cpfcnpj: "83294489654"
              emails: ["exemplo@example.com.br"]
              fones: ["11999999999"]
              cep: 4320040
              logradouro: "Rua Exemplo"
              numero: "999"
              uf: "SP"
              clienteFinal: false
              indicadorIE: 9
      responses:
        '200':
          description: Contato criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo: { type: integer }
                  nome:   { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /v1/contatos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Contatos]
      summary: Detalhar contato
      responses:
        '200':
          description: Dados completos do contato
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContatoDetalhado' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Contatos]
      summary: Editar contato
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ContatoInput' }
      responses:
        '200':
          description: Contato atualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContatoDetalhado' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Contatos]
      summary: Excluir contato
      responses:
        '200':
          description: Contato excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ── Produtos ───────────────────────────────────────────────
  /v1/produtos:
    get:
      tags: [Produtos]
      summary: Listar produtos
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryUpdatedAfter'
        - $ref: '#/components/parameters/queryUpdatedBefore'
        - $ref: '#/components/parameters/queryFields'
      responses:
        '200':
          description: Lista paginada de produtos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/ProdutoBase' }
        '401': { $ref: '#/components/responses/Unauthorized' }
    post:
      tags: [Produtos]
      summary: Criar produto
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProdutoInput' }
      responses:
        '200':
          description: Produto criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:   { type: integer }
                  descricao: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }

  /v1/produtos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Produtos]
      summary: Detalhar produto
      responses:
        '200':
          description: Dados completos do produto
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ProdutoBase' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Produtos]
      summary: Editar produto
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProdutoInput' }
      responses:
        '200':
          description: Produto atualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ProdutoBase' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Produtos]
      summary: Excluir produto
      responses:
        '200':
          description: Produto excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/produtos/{codigo}/fotos:
    post:
      tags: [Produtos]
      summary: Enviar foto do produto
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [foto]
              properties:
                foto:
                  type: string
                  format: binary
                  description: Imagem (máximo 10 MB)
      responses:
        '200':
          description: Foto enviada com sucesso
        '400':
          description: Arquivo inválido ou acima de 10 MB

  # ── Serviços ───────────────────────────────────────────────
  /v1/servicos:
    get:
      tags: [Serviços]
      summary: Listar serviços
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de serviços
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/ServicoBase' }
    post:
      tags: [Serviços]
      summary: Criar serviço
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ServicoInput' }
      responses:
        '200':
          description: Serviço criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:   { type: integer }
                  descricao: { type: string }

  /v1/servicos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Serviços]
      summary: Detalhar serviço
      responses:
        '200':
          description: Dados do serviço
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ServicoBase' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Serviços]
      summary: Editar serviço
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ServicoInput' }
      responses:
        '200':
          description: Serviço atualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ServicoBase' }
    delete:
      tags: [Serviços]
      summary: Excluir serviço
      responses:
        '200':
          description: Serviço excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Vendas ─────────────────────────────────────────────────
  /v1/vendas:
    get:
      tags: [Vendas]
      summary: Listar vendas
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryUpdatedAfter'
        - $ref: '#/components/parameters/queryUpdatedBefore'
        - $ref: '#/components/parameters/queryDtIni'
        - $ref: '#/components/parameters/queryDtFim'
        - $ref: '#/components/parameters/queryFields'
        - name: dtTipo
          in: query
          schema: { type: string, enum: [dtVenda, dtCad, dtEntrega] }
          description: Define qual campo de data usar nos filtros dtIni/dtFim
        - name: situacao
          in: query
          schema: { type: integer, enum: [10, 50] }
        - name: codContato
          in: query
          schema: { type: integer }
      responses:
        '200':
          description: Lista paginada de vendas
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/VendaSummary' }
        '401': { $ref: '#/components/responses/Unauthorized' }
    post:
      tags: [Vendas]
      summary: Criar venda
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendaInput' }
            example:
              codContato: 1
              dtVenda: "2024-01-15"
              codVendedor: 1
              situacao: 50
              customizado:
                xCampo1: "Observações gerais"
              produtos:
                - codProduto: 17
                  quant: 2
                  preco: 150.00
                  vDesc: 0
              financeiros:
                - dtVenc: "2024-02-15"
                  valor: 300.00
      responses:
        '200':
          description: Venda criada
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:      { type: integer }
                  nomeContato: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }

  /v1/vendas/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Vendas]
      summary: Detalhar venda
      parameters:
        - name: listarCanceladas
          in: query
          schema: { type: boolean }
          description: Inclui vendas canceladas
      responses:
        '200':
          description: Dados completos da venda
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VendaDetalhada' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Vendas]
      summary: Atualizar situação / campos customizados da venda
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendaUpdateInput' }
      responses:
        '200':
          description: Venda atualizada
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
    delete:
      tags: [Vendas]
      summary: Excluir venda
      responses:
        '200':
          description: Venda excluída
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/vendas/{codigo}/contato:
    get:
      tags: [Vendas]
      summary: Detalhar contato da venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Dados do contato vinculado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContatoDetalhado' }

  /v1/vendas/{codigo}/gerarNfse:
    get:
      tags: [Vendas, NFSe]
      summary: Gerar NFS-e a partir da venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
        - name: reterIssqn
          in: query
          schema: { type: boolean }
          description: Reter ISSQN na nota
      responses:
        '200':
          description: NFS-e gerada
          content:
            application/json:
              schema:
                type: object
                properties:
                  codNota:      { type: integer }
                  emailCliente: { type: string }

  /v1/vendas/{codigo}/gerarNfseEnviarEmail:
    get:
      tags: [Vendas, NFSe]
      summary: Gerar NFS-e e enviar por e-mail
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
        - name: reterIssqn
          in: query
          schema: { type: boolean }
      responses:
        '200':
          description: NFS-e gerada e e-mail enviado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codNota:      { type: integer }
                  emailCliente: { type: string }
                  emailEnviado: { type: boolean }

  /v1/vendas/{codigo}/gerarNfe:
    post:
      tags: [Vendas, NFe]
      summary: Gerar NF-e a partir da venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                enviar:       { type: boolean, default: false, description: "Transmite automaticamente" }
                contigOffline: { type: boolean }
      responses:
        '200':
          description: NF-e gerada
        '403':
          description: Disponível apenas na versão atualizada do módulo NFe

  /v1/vendas/{codigo}/gerarNfce:
    post:
      tags: [Vendas, NFe]
      summary: Gerar NFC-e a partir da venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                cpfcnpj:          { type: string }
                indPres:          { type: integer }
                codTransportadora: { type: integer }
      responses:
        '200':
          description: NFC-e gerada

  /v1/vendas/{codigo}/nfe:
    get:
      tags: [Vendas, NFe]
      summary: Listar NF-es vinculadas à venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Lista de NF-es da venda
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/NFe' }

  /v1/vendas/{codigo}/mercadoLivre:
    get:
      tags: [Vendas]
      summary: Dados do Mercado Livre vinculados à venda
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Informações da venda no Mercado Livre

  /v1/config/vendas/customizado:
    get:
      tags: [Vendas]
      summary: Labels dos campos customizados de vendas
      description: >
        Retorna a descrição configurada para cada campo personalizado
        (xCampo1 a xCampo4) nas vendas.
      responses:
        '200':
          description: Labels dos campos customizados
          content:
            application/json:
              schema:
                type: object
                properties:
                  xCampo1: { type: string }
                  xCampo2: { type: string }
                  xCampo3: { type: string }
                  xCampo4: { type: string }
              example:
                xCampo1: "Observações gerais"
                xCampo2: "Laudo técnico"

  # ── Recebimentos ───────────────────────────────────────────
  /v1/recebimentos:
    get:
      tags: [Recebimentos]
      summary: Listar recebimentos
      description: |
        **Situações:** `20`=Em aberto · `40`=Recebido
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryUpdatedAfter'
        - $ref: '#/components/parameters/queryUpdatedBefore'
        - $ref: '#/components/parameters/queryDtIni'
        - $ref: '#/components/parameters/queryDtFim'
        - name: situacao
          in: query
          schema: { type: integer, enum: [20, 40] }
      responses:
        '200':
          description: Lista paginada de recebimentos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/FinanceiroSummary' }
    post:
      tags: [Recebimentos]
      summary: Criar recebimento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FinanceiroInput' }
      responses:
        '200':
          description: Recebimento criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:   { type: integer }
                  descricao: { type: string }

  /v1/recebimentos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Recebimentos]
      summary: Detalhar recebimento
      responses:
        '200':
          description: Dados do recebimento
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FinanceiroDetalhado' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Recebimentos]
      summary: Editar recebimento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FinanceiroInput' }
      responses:
        '200':
          description: Recebimento atualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FinanceiroDetalhado' }
    delete:
      tags: [Recebimentos]
      summary: Excluir recebimento
      responses:
        '200':
          description: Recebimento excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  /v1/recebimentos/{codigo}/receber:
    put:
      tags: [Recebimentos]
      summary: Efetivar recebimento (marcar como recebido)
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      requestBody:
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EfetivarInput' }
            example:
              valor: 500.00
              dtPgto: "2024-01-20"
      responses:
        '200':
          description: Recebimento efetivado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
        '403':
          description: Recebimento já foi pago

  # ── Pagamentos ─────────────────────────────────────────────
  /v1/pagamentos:
    get:
      tags: [Pagamentos]
      summary: Listar pagamentos
      description: |
        **Situações:** `10`=Em aberto · `30`=Pago
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryUpdatedAfter'
        - $ref: '#/components/parameters/queryUpdatedBefore'
        - $ref: '#/components/parameters/queryDtIni'
        - $ref: '#/components/parameters/queryDtFim'
        - name: situacao
          in: query
          schema: { type: integer, enum: [10, 30] }
      responses:
        '200':
          description: Lista paginada de pagamentos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/FinanceiroSummary' }
    post:
      tags: [Pagamentos]
      summary: Criar pagamento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FinanceiroInput' }
      responses:
        '200':
          description: Pagamento criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:   { type: integer }
                  descricao: { type: string }

  /v1/pagamentos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Pagamentos]
      summary: Detalhar pagamento
      responses:
        '200':
          description: Dados do pagamento
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FinanceiroDetalhado' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    put:
      tags: [Pagamentos]
      summary: Editar pagamento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FinanceiroInput' }
      responses:
        '200':
          description: Pagamento atualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FinanceiroDetalhado' }
    delete:
      tags: [Pagamentos]
      summary: Excluir pagamento
      responses:
        '200':
          description: Pagamento excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  /v1/pagamentos/{codigo}/pagar:
    put:
      tags: [Pagamentos]
      summary: Efetivar pagamento (marcar como pago)
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      requestBody:
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EfetivarInput' }
            example:
              valor: 200.00
              dtPgto: "2024-01-25"
      responses:
        '200':
          description: Pagamento efetivado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }
        '403':
          description: Pagamento já foi pago

  /v1/pagamentos/{codigo}/contato:
    get:
      tags: [Pagamentos]
      summary: Detalhar contato do pagamento
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Contato vinculado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ContatoDetalhado' }

  # ── Boletos ────────────────────────────────────────────────
  /v1/boletos:
    get:
      tags: [Boletos]
      summary: Listar boletos
      description: |
        Boletos **não** podem ser editados ou excluídos via API.

        **Situações:** `10`=Em aberto · `50`=Recebido
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de boletos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/BoletoSummary' }
    post:
      tags: [Boletos]
      summary: Criar boleto
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BoletoInput' }
            example:
              codContato: 1
              dtVenc: "2024-02-14"
              codRecebimentos: [445, 446]
      responses:
        '200':
          description: Boleto criado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BoletoDetalhado' }
        '400': { $ref: '#/components/responses/BadRequest' }

  /v1/boletos/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Boletos]
      summary: Detalhar boleto
      parameters:
        - name: mostrarInativos
          in: query
          schema: { type: boolean }
          description: Exibe boletos cancelados/inativos
      responses:
        '200':
          description: Dados do boleto
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BoletoDetalhado' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }

  /v1/verificarRetornoBoletos:
    get:
      tags: [Boletos]
      summary: Verificar arquivo de retorno bancário
      description: Processa o retorno bancário e atualiza os boletos pagos.
      responses:
        '200':
          description: Retorno processado

  # ── NFSe ───────────────────────────────────────────────────
  /v1/nfse:
    get:
      tags: [NFSe]
      summary: Listar NFS-es
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de NFS-es
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/NFSe' }
    post:
      tags: [NFSe]
      summary: Emitir NFS-e
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NFSeInput' }
      responses:
        '200':
          description: NFS-e emitida
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NFSe' }

  /v1/nfse/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [NFSe]
      summary: Detalhar NFS-e
      responses:
        '200':
          description: Dados da NFS-e
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NFSe' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ── NFe ────────────────────────────────────────────────────
  /v1/nfe:
    get:
      tags: [NFe]
      summary: Listar NF-es emitidas
      parameters:
        - name: dtIni
          in: query
          required: true
          schema: { type: string, format: date }
        - name: dtFim
          in: query
          required: true
          schema: { type: string, format: date }
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
      responses:
        '200':
          description: Lista paginada de NF-es
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/NFe' }

  /v1/nfe/salvar:
    post:
      tags: [NFe]
      summary: Salvar NF-e a partir de XML
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                xml:      { type: string, description: "Conteúdo XML da NF-e" }
                codVenda: { type: integer, description: "Venda a vincular" }
                enviar:   { type: boolean, description: "Transmite automaticamente" }
      responses:
        '200':
          description: NF-e salva com sucesso

  /v1/nfe/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [NFe]
      summary: Detalhar NF-e
      responses:
        '200':
          description: Dados da NF-e
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NFe' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/nfe/{codigo}/xml:
    get:
      tags: [NFe]
      summary: Download do XML da NF-e
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Arquivo XML
          content:
            application/xml:
              schema: { type: string, format: binary }

  /v1/nfe/{codigo}/danfe:
    get:
      tags: [NFe]
      summary: Download do DANFE (PDF)
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Arquivo PDF do DANFE
          content:
            application/pdf:
              schema: { type: string, format: binary }

  # ── NF-e Recebidas ─────────────────────────────────────────
  /v1/nfe/recebidas:
    get:
      tags: [NFe]
      summary: Listar NF-es recebidas
      parameters:
        - name: dtIni
          in: query
          required: true
          schema: { type: string, format: date }
        - name: dtFim
          in: query
          required: true
          schema: { type: string, format: date }
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
      responses:
        '200':
          description: Lista paginada de NF-es recebidas
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/NFeRecebida' }

  /v1/nfe/buscarNotas:
    get:
      tags: [NFe]
      summary: Buscar novas NF-es na SEFAZ (DFe)
      description: |
        Consulta novos documentos fiscais na SEFAZ via distribuição DFe.
        Importa e salva automaticamente os documentos encontrados.
        Sujeito a cooldown — a SEFAZ exige intervalo mínimo entre consultas.
      responses:
        '200':
          description: Resultado da consulta
          content:
            application/json:
              schema:
                type: object
                properties:
                  cStat:        { type: string, description: "Código de status da SEFAZ" }
                  xMotivo:      { type: string, description: "Descrição do status" }
                  docsGravados: { type: integer, description: "Quantidade de documentos importados" }
        '429':
          description: Cooldown ativo — aguarde o intervalo mínimo entre consultas

  /v1/nfe/recebidas/{id}/xml:
    get:
      tags: [NFe]
      summary: Download do XML de NF-e recebida
      description: |
        Requer que a nota possua XML disponível.
        O XML só está disponível após manifestação de Ciência (`210210`) ou Confirmação (`210200`).
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Arquivo XML da NF-e
          content:
            application/xml:
              schema: { type: string, format: binary }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/nfe/recebidas/{id}/danfe:
    get:
      tags: [NFe]
      summary: Download do DANFE de NF-e recebida (PDF)
      description: |
        Gera o DANFE como visualização de NF-e emitida por terceiros.
        Requer XML disponível (ver `/recebidas/{id}/xml`).
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Arquivo PDF do DANFE
          content:
            application/pdf:
              schema: { type: string, format: binary }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/nfe/recebidas/{id}/manifestar:
    post:
      tags: [NFe]
      summary: Manifestar NF-e recebida
      description: |
        Envia um evento de manifesto do destinatário à SEFAZ.

        | `tpEvento` | Descrição               | `just` obrigatório |
        |------------|-------------------------|--------------------|
        | `210210`   | Ciência da Operação     | Não                |
        | `210200`   | Confirmação da Operação | Não                |
        | `210220`   | Desconhecimento         | Não                |
        | `210240`   | Operação não Realizada  | **Sim** (mín. 15 caracteres) |

        Após Ciência (`210210`), a SEFAZ disponibiliza o XML completo na próxima busca.
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tpEvento]
              properties:
                tpEvento:
                  type: string
                  enum: ['210210', '210200', '210220', '210240']
                  description: "Código do evento de manifesto"
                just:
                  type: string
                  description: "Justificativa (obrigatório para `210240`, mín. 15 caracteres)"
      responses:
        '200':
          description: Retorno da SEFAZ
          content:
            application/json:
              schema:
                type: object
                properties:
                  cStat:   { type: string, description: "Código de status da SEFAZ" }
                  xMotivo: { type: string }
                  retEvento:
                    type: object
                    properties:
                      infEvento:
                        type: object
                        properties:
                          cStat:       { type: string }
                          xMotivo:     { type: string }
                          tpEvento:    { type: string }
                          dhRegEvento: { type: string, format: date-time }
                          nProt:       { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/nfe/recebidas/{id}/lancarCompra:
    post:
      tags: [NFe]
      summary: Lançar compra a partir de NF-e recebida
      description: |
        Importa a NF-e recebida como um pedido de compra (situação **orçamento**).
        Os produtos do XML são mapeados automaticamente para os produtos do sistema quando
        há associação cadastrada para o fornecedor.

        **Notas para consumidor final:** quando a NF-e tem `indFinal=1` e a configuração
        da conta não permite criar compra para consumidor final, o endpoint retorna
        `perguntarConsumidorFinal: 1`. Reenvie com `confirmarConsFinal: true` para forçar a criação.
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                confirmarConsFinal:
                  type: boolean
                  default: false
                  description: "Forçar criação mesmo que a nota seja destinada a consumidor final"
      responses:
        '200':
          description: Compra criada ou confirmação necessária
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: Compra criada com sucesso
                    properties:
                      codXml:    { type: integer, description: "Código do registro XML importado" }
                      codCompra: { type: integer, description: "Código da compra gerada (situação orçamento)" }
                  - type: object
                    description: Nota destinada a consumidor final — aguarda confirmação
                    properties:
                      perguntarConsumidorFinal: { type: integer, enum: [1] }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ── PIX ────────────────────────────────────────────────────
  /v1/pix:
    get:
      tags: [PIX]
      summary: Listar cobranças PIX
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
      responses:
        '200':
          description: Lista paginada de cobranças PIX
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/Pix' }
    post:
      tags: [PIX]
      summary: Criar cobrança PIX
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [codFin]
              properties:
                codFin:
                  type: integer
                  description: Código do recebimento para gerar a cobrança PIX
      responses:
        '200':
          description: Cobrança PIX criada
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Pix' }

  /v1/pix/credencial:
    get:
      tags: [PIX]
      summary: Consultar credenciais PIX configuradas
      responses:
        '200':
          description: Dados da credencial PIX

  /v1/pix/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [PIX]
      summary: Detalhar cobrança PIX
      responses:
        '200':
          description: Dados da cobrança
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Pix' }

  /v1/pix/{codigo}/consultarSituacao:
    get:
      tags: [PIX]
      summary: Consultar situação atual da cobrança PIX
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Situação atualizada da cobrança

  # ── Compras ────────────────────────────────────────────────
  /v1/compras:
    get:
      tags: [Compras]
      summary: Listar compras
      description: |
        **Situações:** `10`=Orçamento · `50`=Compra efetivada
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryDtIni'
        - $ref: '#/components/parameters/queryDtFim'
        - $ref: '#/components/parameters/queryFields'
        - name: dtTipo
          in: query
          schema: { type: string, enum: [dtCompra, dtCad] }
        - name: situacao
          in: query
          schema: { type: integer, enum: [10, 50] }
        - name: nota
          in: query
          schema: { type: string }
        - name: observacoes
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Lista paginada de compras
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/CompraSummary' }
    post:
      tags: [Compras]
      summary: Criar compra
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CompraInput' }
      responses:
        '200':
          description: Compra criada
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo:     { type: integer }
                  codContato: { type: integer }
                  nomeContato: { type: string }
                  dtCompra:   { type: string, format: date }
                  valorTotal: { type: number }

  /v1/compras/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Compras]
      summary: Detalhar compra
      responses:
        '200':
          description: Dados completos da compra
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CompraDetalhada' }
        '404': { $ref: '#/components/responses/NotFound' }
        '410': { $ref: '#/components/responses/Gone' }
    delete:
      tags: [Compras]
      summary: Excluir compra
      responses:
        '200':
          description: Compra excluída
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  /v1/compras/{codigo}/efetivar:
    get:
      tags: [Compras]
      summary: Efetivar compra (Orçamento → Compra)
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
        - name: atualizarPrecoVenda
          in: query
          schema: { type: boolean }
      responses:
        '200':
          description: Compra efetivada
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  /v1/compras/{codigo}/reabrir:
    get:
      tags: [Compras]
      summary: Reabrir compra (Compra → Orçamento)
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Compra reaberta
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  /v1/compras/{codigo}/nfe:
    get:
      tags: [Compras, NFe]
      summary: NF-es vinculadas à compra
      parameters:
        - $ref: '#/components/parameters/pathCodigo'
      responses:
        '200':
          description: Lista de NF-es da compra

  # ── Categorias ─────────────────────────────────────────────
  /v1/categorias:
    get:
      tags: [Categorias]
      summary: Listar categorias
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de categorias
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/Categoria' }
    post:
      tags: [Categorias]
      summary: Criar categoria
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CategoriaInput' }
      responses:
        '200':
          description: Categoria criada
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Categoria' }

  /v1/categorias/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Categorias]
      summary: Detalhar categoria
      responses:
        '200':
          description: Dados da categoria
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Categoria' }
    put:
      tags: [Categorias]
      summary: Editar categoria
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CategoriaInput' }
      responses:
        '200':
          description: Categoria atualizada
    delete:
      tags: [Categorias]
      summary: Excluir categoria
      responses:
        '200':
          description: Categoria excluída
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Usuários ───────────────────────────────────────────────
  /v1/usuarios:
    get:
      tags: [Usuários]
      summary: Listar usuários
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryFields'
        - name: vendedor
          in: query
          schema: { type: boolean }
        - name: usuarioSistema
          in: query
          schema: { type: boolean }
      responses:
        '200':
          description: Lista paginada de usuários
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/UsuarioSummary' }
  /v1/usuarios/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema: { type: integer }
    get:
      tags: [Usuários]
      summary: Detalhar usuário
      responses:
        '200':
          description: Dados do usuário
          content:
            application/json:
              schema: { $ref: '#/components/schemas/UsuarioDetalhado' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ── Disponíveis ────────────────────────────────────────────
  /v1/disponiveis:
    get:
      tags: [Disponíveis]
      summary: Listar contas/caixas financeiros
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/Disponivel' }
    post:
      tags: [Disponíveis]
      summary: Criar conta/caixa
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/DisponiveiInput' }
      responses:
        '200':
          description: Conta/caixa criado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Disponivel' }

  /v1/disponiveis/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Disponíveis]
      summary: Detalhar conta/caixa
      responses:
        '200':
          description: Dados da conta/caixa
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Disponivel' }
    put:
      tags: [Disponíveis]
      summary: Editar conta/caixa
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/DisponiveiInput' }
      responses:
        '200':
          description: Atualizado
    delete:
      tags: [Disponíveis]
      summary: Excluir conta/caixa
      responses:
        '200':
          description: Excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Plano de Contas ────────────────────────────────────────
  /v1/planoContas:
    get:
      tags: [Plano de Contas]
      summary: Listar plano de contas
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de contas contábeis
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/PlanoConta' }
    post:
      tags: [Plano de Contas]
      summary: Criar conta contábil
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PlanoContaInput' }
      responses:
        '200':
          description: Conta criada
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PlanoConta' }

  /v1/planoContas/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Plano de Contas]
      summary: Detalhar conta contábil
      responses:
        '200':
          description: Dados da conta
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PlanoConta' }
    put:
      tags: [Plano de Contas]
      summary: Editar conta contábil
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PlanoContaInput' }
      responses:
        '200':
          description: Conta atualizada
    delete:
      tags: [Plano de Contas]
      summary: Excluir conta contábil
      responses:
        '200':
          description: Excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Formas de Pagamento ────────────────────────────────────
  /v1/formasPagamento:
    get:
      tags: [Formas de Pagamento]
      summary: Listar formas de pagamento
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/FormaPagamento' }
    post:
      tags: [Formas de Pagamento]
      summary: Criar forma de pagamento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FormaPagamentoInput' }
      responses:
        '200':
          description: Criado

  /v1/formasPagamento/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Formas de Pagamento]
      summary: Detalhar forma de pagamento
      responses:
        '200':
          description: Dados
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FormaPagamento' }
    put:
      tags: [Formas de Pagamento]
      summary: Editar forma de pagamento
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/FormaPagamentoInput' }
      responses:
        '200':
          description: Atualizado
    delete:
      tags: [Formas de Pagamento]
      summary: Excluir forma de pagamento
      responses:
        '200':
          description: Excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Ajuste de Estoque ──────────────────────────────────────
  /v1/ajusteEstoque:
    get:
      tags: [Ajuste de Estoque]
      summary: Listar ajustes de estoque
      description: |
        Ajustes de estoque não podem ser editados nem excluídos.

        **Motivos:** `10`=Ajuste geral · `20`=Envio RMA · `30`=Retorno RMA · `40`=Devolução venda · `50`=Devolução compra
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de ajustes
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/AjusteEstoque' }
    post:
      tags: [Ajuste de Estoque]
      summary: Criar ajuste de estoque
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AjusteEstoqueInput' }
            example:
              motivo: 10
              codContato: 1
              obs: "Ajuste por inventário"
              tags: ["inventario"]
              produtos:
                - codProduto: 1
                  estoqueFinal: 300
                - codProduto: 2
                  estoqueFinal: 50
      responses:
        '200':
          description: Ajuste criado
          content:
            application/json:
              schema:
                type: object
                properties:
                  codigo: { type: integer }

  /v1/ajusteEstoque/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Ajuste de Estoque]
      summary: Detalhar ajuste de estoque
      responses:
        '200':
          description: Dados do ajuste
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AjusteEstoque' }

  # ── Disco Virtual ──────────────────────────────────────────
  /v1/discoVirtual:
    get:
      tags: [Disco Virtual]
      summary: Listar arquivos
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
      responses:
        '200':
          description: Lista paginada de arquivos
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/Arquivo' }
    post:
      tags: [Disco Virtual]
      summary: Enviar arquivo
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [arquivo]
              properties:
                arquivo:
                  type: string
                  format: binary
      responses:
        '200':
          description: Arquivo enviado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Arquivo' }

  /v1/discoVirtual/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Disco Virtual]
      summary: Detalhar arquivo
      responses:
        '200':
          description: Dados do arquivo
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Arquivo' }
    delete:
      tags: [Disco Virtual]
      summary: Excluir arquivo
      responses:
        '200':
          description: Arquivo excluído
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResponse' }

  # ── Config. Tributária ─────────────────────────────────────
  /v1/configTrib:
    get:
      tags: [Config. Tributária]
      summary: Listar grupos de tributação
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
      responses:
        '200':
          description: Lista paginada de configurações tributárias
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/ConfigTrib' }

  /v1/configTrib/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Config. Tributária]
      summary: Detalhar configuração tributária
      responses:
        '200':
          description: Dados do grupo de tributação
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ConfigTrib' }

  # ── Devoluções ─────────────────────────────────────────────
  /v1/devolucoes:
    get:
      tags: [Devoluções]
      summary: Listar devoluções
      parameters:
        - $ref: '#/components/parameters/queryFiltro'
        - $ref: '#/components/parameters/queryPage'
        - $ref: '#/components/parameters/queryOrderBy'
        - $ref: '#/components/parameters/queryFields'
      responses:
        '200':
          description: Lista paginada de devoluções
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: '#/components/schemas/Devolucao' }
    post:
      tags: [Devoluções]
      summary: Criar devolução
      description: |
        Permite registrar a devolução de itens de uma venda. É possível:
        - **Editar o recebimento da venda** (via `finReceber`)
        - **Gerar pagamento ao cliente** (via `finPagar`)
        - **Apenas ajustar o estoque** (sem informar `finReceber` nem `finPagar`)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/DevolucaoInput' }
            examples:
              com_finReceber:
                summary: Editando recebimento da venda
                value:
                  codVenda: 22
                  tags: ["tag1"]
                  obs: "Produto com defeito"
                  produtos:
                    - codigo: 8
                      valorDevolvido: 2.50
                      quantDevolvida: 1
                  finReceber:
                    - codigo: 10
                      valor: 116.28
              com_finPagar:
                summary: Gerando pagamento ao cliente
                value:
                  codVenda: 22
                  obs: "Produto com defeito"
                  produtos:
                    - codigo: 8
                      valorDevolvido: 2.50
                      quantDevolvida: 1
                  finPagar:
                    - valor: 18.48
                      data: "2024-01-15"
                      codFormaPgto: 1
                      codDisponivel: 1
                      codPlanoConta: 27
                      situacao: 20
              sem_financeiro:
                summary: Apenas ajuste de estoque
                value:
                  codVenda: 22
                  obs: "Devolução sem ajuste financeiro"
                  produtos:
                    - codigo: 8
                      valorDevolvido: 0
                      quantDevolvida: 1
      responses:
        '200':
          description: Devolução criada
          content:
            application/json:
              schema:
                type: object
                properties:
                  codDevol:  { type: integer }
                  deducoes:  { type: array, items: { type: object } }

  /v1/devolucoes/{codigo}:
    parameters:
      - $ref: '#/components/parameters/pathCodigo'
    get:
      tags: [Devoluções]
      summary: Detalhar devolução
      responses:
        '200':
          description: Dados da devolução
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Devolucao' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ── Webhooks ───────────────────────────────────────────────
  /v1/webhooks:
    get:
      tags: [Webhooks]
      summary: Listar webhooks cadastrados
      responses:
        '200':
          description: Lista de webhooks
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/Webhook' }
    post:
      tags: [Webhooks]
      summary: Cadastrar webhook
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WebhookInput' }
      responses:
        '200':
          description: Webhook cadastrado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Webhook' }

  # ── Relatórios ─────────────────────────────────────────────
  /v1/relatorios/{relatorio}:
    post:
      tags: [Relatórios]
      summary: Gerar relatório
      description: |
        Relatórios disponíveis:

        **Financeiro:** `extratoFinanceiro`, `fluxoDeCaixa`, `fluxoFinanceiro`,
        `dre`, `fluxoFuturo`, `fluxoFuturoDetalhado`, `lancamentosCancelados`,
        `personalizadoFinanceiro`, `financeiroPorVendedor`, `comissoesFinanceiro`,
        `custosDasCobrancas`, `conciliacaoBancaria`, `boletos`

        **Vendas:** `vendasDetalhadas`, `detalhesProdutosVendidos`, `comissoesVendas`,
        `abcProdutosVendidos`, `abcClientesVendas`, `vendasPorTipoDocumento`,
        `vendasSemValores`, `vendasResumo`

        **Compras:** `comprasDetalhadas`, `detalhesProdutosComprados`,
        `producoesDetalhadas`, `composicoes`

        **Contatos:** `historicoContato`, `personalizadoContatos`,
        `aniversariantesDoMes`, `inatividadeClientes`, `contatosDuplicados`

        **Estoque/Produtos:** `produtosPorFornecedor`, `estoqueMinimo`,
        `estoqueDoDia`, `estoqueHistorico`, `sugestaoCompra`, `produtosDuplicados`,
        `movimentacaoProduto`, `personalizadoProdutos`

        **Fiscal:** `nfseImpostos`, `nfeResumo`, `nfeTotaisPorPeriodo`

        **Sistema:** `registroEventos`, `registroAcessos`
      parameters:
        - name: relatorio
          in: path
          required: true
          schema:
            type: string
            enum:
              - extratoFinanceiro
              - fluxoDeCaixa
              - fluxoFinanceiro
              - dre
              - fluxoFuturo
              - fluxoFuturoDetalhado
              - lancamentosCancelados
              - personalizadoFinanceiro
              - financeiroPorVendedor
              - comissoesFinanceiro
              - custosDasCobrancas
              - conciliacaoBancaria
              - boletos
              - vendasDetalhadas
              - detalhesProdutosVendidos
              - comissoesVendas
              - abcProdutosVendidos
              - abcClientesVendas
              - vendasPorTipoDocumento
              - vendasSemValores
              - vendasResumo
              - comprasDetalhadas
              - detalhesProdutosComprados
              - producoesDetalhadas
              - composicoes
              - historicoContato
              - personalizadoContatos
              - aniversariantesDoMes
              - inatividadeClientes
              - contatosDuplicados
              - produtosPorFornecedor
              - estoqueMinimo
              - estoqueDoDia
              - estoqueHistorico
              - sugestaoCompra
              - produtosDuplicados
              - movimentacaoProduto
              - personalizadoProdutos
              - nfseImpostos
              - nfeResumo
              - nfeTotaisPorPeriodo
              - registroEventos
              - registroAcessos
          description: Identificador do relatório
      requestBody:
        content:
          application/json:
            schema:
              type: object
              description: Parâmetros do relatório (variam por tipo)
              properties:
                dtIni: { type: string, format: date }
                dtFim: { type: string, format: date }
      responses:
        '200':
          description: Dados do relatório
          content:
            application/json:
              schema: { type: object }

  # ── Empresa ────────────────────────────────────────────────
  /v1/empresa:
    get:
      tags: [Empresa]
      summary: Dados cadastrais da empresa
      responses:
        '200':
          description: Dados da empresa
          content:
            application/json:
              schema:
                type: object
                properties:
                  razaoSocial: { type: string }
                  fantasia:    { type: string }
                  cnpj:        { type: string }
                  ie:          { type: string }
                  im:          { type: string }
                  email:       { type: string }
                  fone:        { type: string }
                  logradouro:  { type: string }
                  numero:      { type: string }
                  complemento: { type: string }
                  bairro:      { type: string }
                  cidade:      { type: string }
                  uf:          { type: string }
                  cep:         { type: string }
