Aplicação web para visualização, conferência e exportação de borderôs financeiros e operacionais.
Super eventos, mega experiências
A aplicação também poderá ser acessada pela versão hospedada na Vercel:
Link da Vercel: será adicionado após a publicação e a validação do ambiente de produção.
A execução local continua disponível pelas instruções da seção Como executar.
Este projeto foi desenvolvido para a SuperTixs. A aplicação consolida dados reais anonimizados de três eventos e gera um borderô completo, com informações financeiras, operacionais e detalhamento ingresso a ingresso.
Os arquivos JSON da pasta data/ são a única fonte de dados.
A solução permite:
- selecionar um dos três eventos disponíveis;
- consultar o borderô completo pela página web;
- filtrar as vendas por data inicial e final;
- navegar pelo detalhamento paginado dos ingressos;
- exportar o mesmo relatório em PDF e XLSX;
- manter o período selecionado nas exportações;
- validar automaticamente os dados de entrada e os resultados consolidados.
| Evento | Perfil |
|---|---|
evento-demo-a |
Evento corporativo, ingressos de valor elevado, grande quantidade de cortesias e formulário personalizado |
evento-demo-b |
Evento cultural com cupons, cortesias, reembolsos, transferências e check-ins |
evento-demo-c |
Festival de maior volume, com ingressos gratuitos e pagos, cupons e muitos check-ins |
Cada evento possui os seguintes arquivos:
event.json
ticket-types.json
coupons.json
transactions.json
sold-tickets.json
O significado dos campos e as particularidades dos dados estão descritos em docs/dicionario-de-dados.md.
A página web, o PDF e o XLSX apresentam as oito seções exigidas em docs/requisitos-bordero.md:
- Resumo do evento — vendas brutas, taxas, vendas líquidas, repasse, ticket médio, percentual vendido e percentual de check-ins;
- Finanças gerais — composição dos valores brutos, taxas e valores líquidos;
- Formas de pagamento — quantidade, valor e participação por método;
- Tipos e lotes de ingresso — estoque, vendas, descontos, taxas e valores líquidos;
- Cupons de desconto — tipo, valor, utilizações, desconto concedido e receita associada;
- Cortesias — ingressos emitidos, cancelados, válidos e check-ins;
- Disponibilidade e check-ins — estoque, vendas e presença por tipo e lote;
- Detalhamento — listagem completa, ingresso a ingresso.
As regras foram centralizadas para manter os mesmos resultados na web, no PDF e no XLSX:
- status são normalizados antes das comparações;
- transações canceladas, inválidas ou reembolsadas não entram na receita;
- pagamentos
FREEeCOMPLIMENTARYficam fora da receita financeira; - a receita bruta utiliza o campo
totaldas transações válidas; - as taxas utilizam o valor informado em
platformFee, sem inferir ou recalcular valores ausentes; - a regra
absorbFeesdefine se a taxa é absorvida pelo organizador ou paga pelo comprador; - o ticket médio considera somente ingressos efetivamente pagos;
- valores monetários são calculados com
decimal.jse convertidos para centavos com arredondamento controlado; - descontos e taxas distribuídos por ingresso utilizam rateio de centavos para evitar diferenças de arredondamento;
- o filtro de período considera
transaction.createdAt; - o detalhamento mantém transações canceladas ou reembolsadas para rastreabilidade, mas identifica corretamente seu status.
| Tecnologia | Uso |
|---|---|
| Next.js 16 | Aplicação web, rotas dinâmicas e endpoints de exportação |
| React 19 | Componentes e interface |
| TypeScript 6 | Tipagem e regras de domínio |
| Node.js | Leitura dos arquivos, processamento e geração dos relatórios |
| Zod | Validação estrutural dos JSONs |
| Decimal.js | Cálculos monetários com precisão decimal |
| PDFKit | Geração do relatório em PDF |
| ExcelJS | Geração da planilha XLSX |
| ESLint | Análise estática do código |
| TSX | Execução dos validadores TypeScript |
O desenvolvimento foi realizado e testado principalmente com:
Node.js 22.21.0
npm 10.9.4
assets/ Logos e assinatura
data/ Dados dos três eventos
docs/ Requisitos, dicionário e validações
scripts/
validate-data.ts Validação dos arquivos de entrada
validate-reports.ts Validação dos resultados dos relatórios
src/
app/ Páginas e rotas da aplicação
components/ Tabelas e ações reutilizáveis
lib/
bordero/ Regras de cálculo e consolidação
data/ Leitura e validação dos JSONs
export/ Geração de PDF e XLSX
formatting/ Formatação de datas, moedas e percentuais
money/ Operações monetárias
types/ Tipos do domínio
- Node.js 22 ou versão compatível;
- npm;
- Git, caso o projeto seja obtido pelo repositório.
Nenhuma variável de ambiente é obrigatória.
Com o repositório clonado, abra um terminal na raiz do projeto e execute:
npm ciO uso de npm ci é recomendado porque instala exatamente as versões registradas no package-lock.json.
npm run devA aplicação estará disponível em:
http://localhost:3000
O script de desenvolvimento utiliza Webpack:
next dev --webpack
Essa escolha foi feita por apresentar comportamento mais estável para o CSS e para os pacotes de geração de arquivos utilizados no projeto.
npm run build
npm run startnpm run lint
npm run validate:data
npm run validate:reports
npm run buildFunção de cada comando:
| Comando | Finalidade |
|---|---|
npm run lint |
Verifica problemas de estilo e código |
npm run validate:data |
Confere a estrutura e características esperadas dos JSONs |
npm run validate:reports |
Recalcula e valida os resultados consolidados dos três eventos |
npm run build |
Valida a compilação de produção do Next.js |
/ Seleção dos eventos
/eventos/[slug] Borderô do evento
/api/eventos/[slug]/pdf Exportação em PDF
/api/eventos/[slug]/xlsx Exportação em XLSX
Os parâmetros de período utilizados na página também são enviados para as exportações:
?inicio=AAAA-MM-DD&fim=AAAA-MM-DD
O PDF é gerado com PDFKit e inclui:
- cabeçalho e identidade visual;
- uma seção por conjunto de informações;
- tabelas com quebras automáticas de página;
- período aplicado;
- data de emissão;
- numeração de páginas.
As seções convencionais utilizam formato A4. A seção de detalhamento ingresso a ingresso utiliza A3, pois a quantidade de colunas tornava o conteúdo excessivamente pequeno e pouco legível em A4.
A planilha é gerada com ExcelJS e possui uma aba para cada seção do borderô. As células recebem formatação de moeda, percentual, data, cabeçalhos, totais e dimensões adequadas para leitura.
O PDFKit depende de arquivos internos de fonte. Quando empacotado diretamente pelo Next.js, o caminho desses arquivos podia ser perdido, resultando em erro ao localizar Helvetica.afm.
Solução: o pacote foi mantido como dependência externa do servidor em next.config.ts:
serverExternalPackages: ["pdfkit"]Assim, o PDFKit é executado com a própria estrutura de arquivos disponível no ambiente Node.js.
Ao posicionar o rodapé abaixo da área útil, o PDFKit interpretava o texto como conteúdo adicional e criava novas páginas automaticamente.
Solução: o rodapé passou a ser desenhado dentro da margem útil da página, com altura limitada e lineBreak: false. As páginas também são mantidas em buffer para que a numeração seja inserida somente depois de o total ser conhecido.
Durante o desenvolvimento ocorreram falhas relacionadas ao manifesto do React e diferenças na aplicação do CSS com o Turbopack.
Solução: o servidor de desenvolvimento passou a usar Webpack por meio de next dev --webpack, que apresentou comportamento mais previsível neste projeto.
O uso direto de number em JavaScript pode gerar imprecisões binárias, especialmente em somas, rateios e arredondamentos financeiros.
Solução: os cálculos foram implementados com decimal.js, com conversão controlada para centavos e arredondamento ROUND_HALF_UP.
Os arquivos representam perspectivas diferentes do mesmo processo: a transação contém o pedido consolidado, enquanto sold-tickets.json contém documentos individuais. Nem sempre os dados possuem correspondência direta perfeita.
Solução: as transações são usadas como fonte financeira e os ingressos emitidos como fonte de titularidade, check-in e identificação individual. O cruzamento é realizado por transactionId e, quando necessário, por tipo, título e lote.
Alguns registros de ingresso não possuem o identificador direto do tipo de ingresso.
Solução: foi implementada uma estratégia de resolução em etapas:
tidouticketTypeIdquando disponível;- combinação exata de título e lote;
- correspondência pelo título;
- único item da transação, quando não há ambiguidade.
Os dados possuem status em diferentes caixas e campos auxiliares de cancelamento ou reembolso.
Solução: os valores são normalizados com remoção de espaços e conversão para minúsculas. A decisão final também considera flags e datas como isCancelled, cancelledAt, isRefunded e refundedAt.
Foram encontrados registros marcados como RECEIVED, mas também sinalizados como reembolsados ou cancelados.
Solução: as flags e datas de invalidação têm prioridade sobre o status de pagamento. A operação continua visível no detalhamento, mas deixa de compor receitas, taxas, vendas e ticket médio.
Pedidos podiam conter simultaneamente itens pagos e gratuitos. Contar todos os itens no denominador reduzia incorretamente o ticket médio.
Solução: o denominador passou a considerar apenas itens cujo preço original, preço ou desconto aplicado demonstre que o ingresso possuía valor financeiro.
A quantidade de ingressos tornava inviável exibir todos os registros de uma só vez na página web.
Solução: o detalhamento foi dividido em páginas de 50 registros, com navegação que preserva o evento e o período selecionado.
Inicialmente, a página podia estar filtrada, mas os downloads eram gerados com todo o período.
Solução: os parâmetros inicio e fim passaram a ser adicionados às URLs de PDF e XLSX e são processados novamente pelas rotas de exportação.
Erros de leitura, JSON incompleto ou dados fora do formato esperado podiam produzir mensagens pouco informativas.
Solução: o carregador passou a separar as etapas de leitura, JSON.parse e validação com Zod. Cada etapa gera uma mensagem contextual contendo o arquivo e a causa do problema.
A distribuição direta de descontos e taxas entre vários ingressos podia produzir diferenças de um ou mais centavos em relação ao total da transação.
Solução: foi criado um rateio em centavos que distribui os resíduos de arredondamento sem alterar o total consolidado.
Os itens abaixo não impedem as funções principais da aplicação, mas devem ser considerados durante a avaliação:
O ExcelJS posiciona imagens como objetos flutuantes por coordenadas fracionárias. A renderização pode variar conforme o Excel, o LibreOffice, as células mescladas e a largura calculada das colunas.
A logo é inserida e dimensionada corretamente, mas pode apresentar um pequeno desalinhamento horizontal em algumas abas ou programas. As tentativas de correção por deslocamento manual não produziram comportamento consistente em todos os casos.
Foi observado, de forma não determinística durante o desenvolvimento, o erro:
SyntaxError: Unexpected end of JSON input
O carregador possui tratamento explícito para falhas de leitura, interpretação e validação. Ainda assim, o erro pode surgir esporadicamente em situações de atualização do servidor de desenvolvimento ou leitura interrompida. Não foi encontrada uma reprodução estável que permitisse uma correção definitiva.
Existem divergências naturais entre arquivos, campos opcionais e transações sem platformFee. Nesses casos, a aplicação segue literalmente o dicionário de dados e não inventa valores ausentes.
Por esse motivo, algumas reconciliações entre seções podem apresentar pequenas diferenças que refletem os dados fornecidos, não um recálculo inferido pela aplicação.
- Os JSONs permanecem imutáveis e são tratados como fonte oficial.
- As regras de negócio ficam em
src/lib/bordero, separadas da interface. - A web e as duas exportações recebem o mesmo objeto consolidado para reduzir divergências.
- O detalhamento permanece completo nos arquivos exportados, embora seja paginado na web.
- Campos ausentes recebem tratamentos seguros quando o requisito permite, sem criar dados financeiros inexistentes.
- O layout foi desenvolvido originalmente para a identidade da SuperTixs, sem copiar relatórios de outras plataformas.
docs/requisitos-bordero.md— conteúdo e regras exigidas;docs/dicionario-de-dados.md— descrição dos arquivos e campos;docs/identidade-visual.md— orientações visuais;docs/validacao.md— características e números esperados dos conjuntos de dados.
Esta cópia contém dados sintéticos/pseudonimizados para publicação demonstrativa. Foram substituídos nomes de eventos, produtores e participantes, e-mails, telefones, documentos, endereços, respostas de formulários, cupons, URLs, identificadores internos e datas. Valores monetários também foram transformados por fatores distintos por evento, mantendo a estrutura e os relacionamentos necessários ao funcionamento do relatório.
Os arquivos originais não devem ser reintroduzidos nesta versão pública.