From 13d6c243c36e80745d726c9124838908ebe0ce1c11dd1af21600abce946eaebc Mon Sep 17 00:00:00 2001 From: victor Date: Thu, 18 Jun 2026 20:15:05 -0300 Subject: [PATCH] =?UTF-8?q?Corre=C3=A7=C3=A3o=20do=20PDF=20QRCODE=20holeri?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++++++++++++++ app/services/pdf/base_pdf.rb | 2 +- app/services/pdf/holerite_pdf.rb | 20 ++++++++++---------- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6be7ed6..8493ee6 100644 --- a/README.md +++ b/README.md @@ -591,3 +591,28 @@ Conjunto de melhorias para deixar o sistema pronto para entrega final. 20260617000002_add_operacoes_to_consolidacoes.rb ``` Aplicar com: `docker-compose exec app bundle exec rails db:migrate` + +--- + +## 🔧 Correções — Holerite PDF (18/06/2026) + +### 🔴 Bugs corrigidos +- **500 ao gerar holerite** (`gerar_pdf_holerite`): `Prawn::Errors::IncompatibleStringEncoding` + (`Encoding::UndefinedConversionError`). Os PDFs usavam a fonte **embutida** do Prawn + (Helvetica/AFM), que só aceita o charset **Windows-1252** — o caractere **`✓`** do selo + "✓ PAGO" não existe nesse charset e derrubava a geração. + ✅ **Corrigido:** `BasePdf` agora registra a família **DejaVu Sans (TTF)** e a define como + fonte padrão (`registrar_fonte_utf8`), com suporte total a **UTF-8**. Vale para todos os PDFs + (holerite e relatório) — qualquer caractere Unicode passa a funcionar. + - Fontes versionadas em `app/assets/fonts/DejaVuSans.ttf` e `DejaVuSans-Bold.ttf` + (a imagem Docker `ruby:3.2.2-slim` não traz fontes do sistema; o `COPY . .` do Dockerfile e + o volume `.:/app` garantem que estejam disponíveis em runtime). +- **Assinaturas do holerite caíam na 2ª página:** após o QR Code o cursor ficava abaixo do + limite de quebra (`start_new_page if cursor < 120`), empurrando o bloco de assinaturas para + uma página nova. ✅ **Corrigido:** espaçamentos do holerite compactados (selo, tabelas, caixa + do total, bloco do QR menor) + folga antes das assinaturas reduzida (`BasePdf#assinaturas`, + 60 → 36) — **o holerite agora cabe em uma única página**. + +### 📌 Padrão (novo) +- **PDFs com Prawn**: usar **sempre** a fonte TTF registrada no `BasePdf` (DejaVu, UTF-8). Nunca + contar com a fonte embutida — ela quebra em qualquer caractere fora do Windows-1252. diff --git a/app/services/pdf/base_pdf.rb b/app/services/pdf/base_pdf.rb index 1c2f2c9..adece85 100644 --- a/app/services/pdf/base_pdf.rb +++ b/app/services/pdf/base_pdf.rb @@ -90,7 +90,7 @@ module Pdf # (direita). Garante espaço criando nova página se o rodapé estiver perto. def assinaturas(motorista: nil) @pdf.start_new_page if @pdf.cursor < 120 - @pdf.move_down 60 + @pdf.move_down 36 metade = (@pdf.bounds.width - 40) / 2.0 topo = @pdf.cursor diff --git a/app/services/pdf/holerite_pdf.rb b/app/services/pdf/holerite_pdf.rb index df83379..94e74ec 100644 --- a/app/services/pdf/holerite_pdf.rb +++ b/app/services/pdf/holerite_pdf.rb @@ -34,7 +34,7 @@ module Pdf ['Período', "#{@consolidacao.data_inicio.strftime('%d/%m/%Y')} a #{@consolidacao.data_fim.strftime('%d/%m/%Y')}"], ['Emissão', Time.current.strftime('%d/%m/%Y')] ] - @pdf.table(info, width: @pdf.bounds.width, cell_style: { size: 10, padding: [5, 8] }) do |t| + @pdf.table(info, width: @pdf.bounds.width, cell_style: { size: 10, padding: [4, 8] }) do |t| t.columns(0).font_style = :bold t.columns(0).background_color = 'F3F4F6' t.columns(0).width = 120 @@ -57,7 +57,7 @@ module Pdf linhas = [['Descrição', 'Qtd', 'Unit.', 'Valor']] + por_tipo @pdf.table(linhas, header: true, width: @pdf.bounds.width, - cell_style: { size: 10, padding: [6, 8] }) do |t| + cell_style: { size: 10, padding: [4, 8] }) do |t| t.row(0).background_color = PRETO t.row(0).text_color = 'FFFFFF' t.row(0).font_style = :bold @@ -67,7 +67,7 @@ module Pdf total = @entregas.sum { |e| e.desconto? ? -e.valor_aplicado : e.valor_aplicado } # Caixa do total — destaque laranja - @pdf.move_down 16 + @pdf.move_down 12 @pdf.fill_color LARANJA @pdf.fill_rounded_rectangle [0, @pdf.cursor], @pdf.bounds.width, 50, 8 @pdf.fill_color '000000' @@ -75,7 +75,7 @@ module Pdf at: [16, @pdf.cursor - 10], size: 10, style: :bold @pdf.text_box moeda(total), at: [16, @pdf.cursor - 24], size: 22, style: :bold - @pdf.move_down 58 + @pdf.move_down 48 # Detalhamento: total de entregas e veículos atendidos no período total_entregas = @entregas.count @@ -84,7 +84,7 @@ module Pdf @pdf.text "Total de entregas: #{total_entregas}", size: 9 @pdf.text "Veículos atendidos: #{veiculos.any? ? veiculos.join(', ') : '—'}", size: 9 @pdf.fill_color '000000' - @pdf.move_down 6 + @pdf.move_down 4 # QR Code de login rápido. O Prawn não pagina imagem sozinho: se faltar # espaço, garantimos uma nova página para o QR não sumir no rodapé. @@ -113,7 +113,7 @@ module Pdf @pdf.fill_color(pago ? 'FFFFFF' : '374151') @pdf.text_box texto, at: [12, @pdf.cursor - 8], width: 238, size: 10, style: :bold @pdf.fill_color '000000' - @pdf.move_down 34 + @pdf.move_down 20 end # Monta a URL respeitando o esquema do APP_HOST. Se vier sem http(s):// @@ -137,13 +137,13 @@ module Pdf arquivo.rewind secao "ACESSO RÁPIDO AO SEU PAINEL" - @pdf.image arquivo.path, width: 110, position: :left - @pdf.move_up 95 + @pdf.image arquivo.path, width: 92, position: :left + @pdf.move_up 80 @pdf.text_box "Escaneie este QR Code com a câmera do celular para entrar\n" \ "no seu painel e acompanhar seus pagamentos.\n\n" \ "Ou acesse: #{@app_host.to_s.sub(%r{\Ahttps?://}, '').chomp('/')}/motorista\ne use seu PIN de 4 dígitos.", - at: [130, @pdf.cursor], width: @pdf.bounds.width - 130, size: 9 - @pdf.move_down 100 + at: [120, @pdf.cursor], width: @pdf.bounds.width - 120, size: 9 + @pdf.move_down 85 ensure arquivo&.close arquivo&.unlink