diff --git a/Imagens para implantação /Imagem colada (5).png b/Imagens para implantação /Imagem colada (5).png new file mode 100644 index 0000000..1b3b70c Binary files /dev/null and b/Imagens para implantação /Imagem colada (5).png differ diff --git a/Imagens para implantação /Imagem colada (6).png b/Imagens para implantação /Imagem colada (6).png new file mode 100644 index 0000000..0fdef96 Binary files /dev/null and b/Imagens para implantação /Imagem colada (6).png differ diff --git a/app/services/analytics/planilha_entregas_xlsx.rb b/app/services/analytics/planilha_entregas_xlsx.rb index ba1111b..dba9da2 100644 --- a/app/services/analytics/planilha_entregas_xlsx.rb +++ b/app/services/analytics/planilha_entregas_xlsx.rb @@ -2,8 +2,12 @@ # # Gera o binário .xlsx da planilha Entregas do cliente com as 3 abas do modelo # "Entregas SUDESTE MM.AAAA_FINAL.xlsx": RESUMO, ENTREGAS e SimpliRoute. -# Os dados vêm de Analytics::PlanilhaEntregas. (Os gráficos embutidos do modelo -# não são replicados — apenas os quadros de dados.) +# Os dados vêm de Analytics::PlanilhaEntregas. +# +# As cores/estilos foram extraídos do próprio arquivo modelo (styles.xml): +# ENTREGAS A..T azul 1155CC e U..Z vermelho C00000 (texto branco, negrito); +# RESUMO com quadros em 002060/0070C0/C00000/A5A5A5; SimpliRoute sem estilo. +# (Os gráficos embutidos do modelo não são replicados.) require 'caxlsx' module Analytics @@ -16,9 +20,7 @@ module Analytics def gerar pkg = Axlsx::Package.new wb = pkg.workbook - @negrito = wb.styles.add_style(b: true) - @titulo = wb.styles.add_style(b: true, sz: 12) - @pct = wb.styles.add_style(format_code: '0.0%') + definir_estilos(wb) aba_resumo(wb) aba_entregas(wb) @@ -29,6 +31,43 @@ module Analytics private + def definir_estilos(wb) + st = wb.styles + borda = { style: :thin, color: 'FF808080', edges: %i[left right top bottom] } + centro = { horizontal: :center, vertical: :center } + + # Cabeçalhos/civis do RESUMO + @titulo_mes = st.add_style(b: true) + @titulo_sec = st.add_style(b: true, sz: 16, u: true) + @rotulo_kpi = st.add_style(b: true, sz: 10) + @valor_kpi = st.add_style(b: true, sz: 10, border: { style: :thin, color: 'FF000000', edges: [:bottom] }) + @valor_pct = st.add_style(b: true, sz: 10, format_code: '0.0%', + border: { style: :thin, color: 'FF000000', edges: [:bottom] }) + + @h_navy = st.add_style(bg_color: '002060', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro) + @h_azul = st.add_style(bg_color: '0070C0', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro) + @h_verm = st.add_style(bg_color: 'C00000', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro) + @h_cinza = st.add_style(bg_color: 'A5A5A5', fg_color: 'FFFFFF', sz: 10, border: borda, + alignment: centro.merge(wrap_text: true)) + @h_motivo = st.add_style(b: true, sz: 10, border: borda, alignment: centro.merge(wrap_text: true)) + + @celula = st.add_style(sz: 11, border: borda) + @cel_num = st.add_style(sz: 11, border: borda, alignment: centro) + + # Status das Entregas / Conferência Documentos (verde "bom", laranja "em + # andamento", azul "a iniciar" — como no modelo) + @st_verde = st.add_style(bg_color: 'C6EFCE', fg_color: '006100', b: true, sz: 10, border: borda, alignment: centro) + @st_laranja = st.add_style(bg_color: 'FCE4D6', fg_color: 'ED7D31', sz: 10, border: borda, alignment: centro) + @st_iniciar = st.add_style(fg_color: '0070C0', sz: 10, border: borda, alignment: centro) + + @perf_rotulo = st.add_style(i: true) + @perf_pct = st.add_style(i: true, format_code: '0.0%') + + # Cabeçalho da ENTREGAS + @cab_azul = st.add_style(bg_color: '1155CC', fg_color: 'FFFFFF', b: true) + @cab_verm = st.add_style(bg_color: 'C00000', fg_color: 'FFFFFF', b: true) + end + # ── RESUMO ─────────────────────────────────────────────────── # Mesma disposição do modelo: mês, totais/performance e um quadro por grupo # (EMAD e UBS) com as coordenadorias fixas + motivos de "Não Entregue". @@ -37,24 +76,35 @@ module Analytics resumo = @planilha.resumo wb.add_worksheet(name: 'RESUMO') do |s| s.add_row [] - s.add_row ['', resumo[:mes]], style: @titulo + s.add_row ['', resumo[:mes]], style: [nil, @titulo_mes] s.add_row [] - s.add_row ['', 'Total Previsto:', resumo[:total_previsto]] - s.add_row ['', 'Total Realizado:', resumo[:total_realizado]] + s.add_row ['', 'Total Previsto:', resumo[:total_previsto]], style: [nil, @rotulo_kpi, @valor_kpi] + s.add_row ['', 'Total Realizado:', resumo[:total_realizado]], style: [nil, @rotulo_kpi, @valor_kpi] s.add_row ['', 'Performance:', resumo[:performance] || ''], - style: [nil, nil, (resumo[:performance] ? @pct : nil)] + style: [nil, @rotulo_kpi, (resumo[:performance] ? @valor_pct : @valor_kpi)] PlanilhaEntregas::GRUPOS.each { |grupo| quadro_grupo(s, grupo, resumo[:grupos][grupo]) } + + s.column_widths 3, 18, 10, 10, 12, 14, 14, 3, 10, 12, 14, 10, 14, 10 end end def quadro_grupo(s, grupo, coords) s.add_row [] - s.add_row ['', grupo], style: @titulo - s.add_row ['', '', '', '', '', 'Informações', '', '', 'Motivos de "Não Entregue"'], style: @negrito + s.add_row ['', grupo], style: [nil, @titulo_sec] + + # "Informações" (F:G) e 'Motivos de "Não Entregue"' (I:N) mescladas + s.add_row ['', '', '', '', '', 'Informações', '', '', 'Motivos de "Não Entregue"', '', '', '', '', ''], + style: [nil, nil, nil, nil, nil, @h_motivo, @h_motivo, nil, *Array.new(6, @h_motivo)] + r = s.rows.size + s.merge_cells("F#{r}:G#{r}") + s.merge_cells("I#{r}:N#{r}") + s.add_row ['', 'Cordenadoria', 'Previsão', 'Entregue', 'Não Entregue', 'Status das Entregas', 'Conferência Documentos', '', - *PlanilhaEntregas::MOTIVOS], style: @negrito + *PlanilhaEntregas::MOTIVOS], + style: [nil, @h_navy, @h_azul, @h_navy, @h_verm, @h_cinza, @h_cinza, nil, + *Array.new(PlanilhaEntregas::MOTIVOS.size, @h_motivo)] # Coordenadorias fixas do modelo + eventuais extras vindas dos dados. nomes = PlanilhaEntregas::COORDENADORIAS + (coords.keys - PlanilhaEntregas::COORDENADORIAS) @@ -70,19 +120,24 @@ module Analytics s.add_row ['', nome, b[:previsao], b[:entregue], b[:nao_entregue], (b[:pendente].zero? ? 'Concluído' : 'Em andamento'), 'A iniciar', '', - *PlanilhaEntregas::MOTIVOS.map { |m| b[:motivos][m] }] + *PlanilhaEntregas::MOTIVOS.map { |m| b[:motivos][m] }], + style: [nil, @celula, @cel_num, @cel_num, @cel_num, + (b[:pendente].zero? ? @st_verde : @st_laranja), @st_iniciar, nil, + *Array.new(PlanilhaEntregas::MOTIVOS.size, @cel_num)] end s.add_row ['', 'Total', total[:previsao], total[:entregue], total[:nao_entregue], '', '', '', - *PlanilhaEntregas::MOTIVOS.map { |m| total[:motivos][m] }], style: @negrito + *PlanilhaEntregas::MOTIVOS.map { |m| total[:motivos][m] }], + style: [nil, @h_navy, @h_azul, @h_navy, @h_verm, nil, nil, nil, + *Array.new(PlanilhaEntregas::MOTIVOS.size, @h_motivo)] if total[:previsao].positive? s.add_row ['', 'Performance %', 1, total[:entregue].to_f / total[:previsao], total[:nao_entregue].to_f / total[:previsao]], - style: [nil, @negrito, @pct, @pct, @pct] + style: [nil, @perf_rotulo, @perf_pct, @perf_pct, @perf_pct] else - s.add_row ['', 'Performance %', '', '', ''], style: [nil, @negrito] + s.add_row ['', 'Performance %', '', '', ''], style: [nil, @perf_rotulo] end end @@ -90,10 +145,14 @@ module Analytics # A..V direto da tabela gade (texto); W..Z do rastreio: # W STATUS (completed/failed...), X ENTREGA (Sim/Não), Y DATA OCORRÊNCIA # (data real do checkout), Z OCORRÊNCIA (motivo do insucesso). + # Cabeçalho como no modelo: A..T azul, U..Z (resultado) vermelho. def aba_entregas(wb) linhas = @planilha.linhas + azuis = PlanilhaEntregas::COLUNAS_GADE.size - 2 # A..T wb.add_worksheet(name: 'ENTREGAS') do |sheet| - sheet.add_row(PlanilhaEntregas::CABECALHO, style: @negrito) + sheet.add_row(PlanilhaEntregas::CABECALHO, + style: Array.new(azuis, @cab_azul) + + Array.new(PlanilhaEntregas::CABECALHO.size - azuis, @cab_verm)) linhas.each do |l| valores, tipos = linha_entrega(l) sheet.add_row(valores, types: tipos) @@ -122,12 +181,13 @@ module Analytics # ── SimpliRoute ────────────────────────────────────────────── # Dump cru do rastreio (todas as visitas das NFs da operação), com o - # cabeçalho exato do modelo; cabeçalho sem coluna correspondente sai vazio. + # cabeçalho exato do modelo — sem estilo, como no original; cabeçalho sem + # coluna correspondente sai vazio. def aba_simpliroute(wb) colunas = @planilha.colunas_rastreio linhas = @planilha.linhas_rastreio wb.add_worksheet(name: 'SimpliRoute') do |sheet| - sheet.add_row(colunas.keys, style: @negrito) + sheet.add_row(colunas.keys) linhas.each do |r| sheet.add_row(colunas.values.map { |col| col ? texto(r[col]) : '' }, types: Array.new(colunas.size, :string))