Files
Reem-Notas/app/services/simpli_route/planilha_carga_xlsx.rb
victor b2695cd920 feat: gerar planilha de carga do SimpliRoute (download .xlsx)
Nova tela ADM/gerente (Administração > Planilha SimpliRoute) que gera a
planilha de importação (.xlsx) de uma operação vigente, no formato do
pt_br_example.xlsx. Preenche Latitude/Longitude reaproveitando a
geocodificação do mês anterior da mesma operação (casa nome + endereço +
supervisão; geo vem do rastreio db_reem_simplerout_2026). Pacientes novos
saem sem lat/long.

- SimpliRoute::PlanilhaCarga: monta linhas + resolve match do mês anterior
- SimpliRoute::PlanilhaCargaXlsx: builder caxlsx com cabeçalho verbatim
- Admin::PlanilhasSimpliRouteController (show/baixar) + policy admin/gerente
- Gem caxlsx (rebuild do container atualiza o Gemfile.lock)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 11:05:24 -03:00

94 lines
2.8 KiB
Ruby

# app/services/simpli_route/planilha_carga_xlsx.rb
#
# Gera o binário .xlsx da carga do SimpliRoute a partir das linhas montadas pelo
# PlanilhaCarga, replicando EXATAMENTE o cabeçalho do arquivo de referência
# (Imagens para implantação/pt_br_example.xlsx) — 26 colunas A..Z.
require 'caxlsx'
module SimpliRoute
class PlanilhaCargaXlsx
# Cabeçalho verbatim do exemplo (inclui os espaços duplos em A e B).
CABECALHO = [
"Titulo* Solicitado",
"endereço completo* Solicitado",
"Carga",
"Janela de horário inicial",
"Janela de horário final",
"Tempo de serviço",
"Anotações",
"Latitude",
"Longitude",
"Identificação de referência",
"Habilidade necessária",
"Habilidade opcional",
"Pessoa de contato",
"Telefone de contato",
"Janela de horário inicial 2",
"Janela de horário final 2",
"Capacidade 2",
"Capacidade 3",
"Prioridade",
"SMS",
"Correio eletrônico de contato",
"Carga pick",
"Carga pick 2",
"Carga pick 3",
"Data agendada",
"Tipo de visita"
].freeze
# Colunas O..Z (14ª em diante) ficam vazias — 12 células.
VAZIAS_FINAIS = Array.new(CABECALHO.size - 14, '').freeze
def initialize(linhas)
@linhas = linhas
end
# String binária do .xlsx.
def gerar
pkg = Axlsx::Package.new
pkg.workbook.add_worksheet(name: 'Carga') do |sheet|
sheet.add_row(CABECALHO)
@linhas.each do |l|
valores, tipos = linha(l)
sheet.add_row(valores, types: tipos)
end
end
pkg.to_stream.read
end
private
# Monta [valores, tipos] de uma linha (A..Z). Lat/long numéricos quando há
# match; D/E como texto ("08:00"/"18:00"); NF/ref como texto (igual ao exemplo).
def linha(l)
lat = l[:latitude]
lng = l[:longitude]
valores = [
l[:titulo].to_s, # A
l[:endereco].to_s, # B
l[:carga], # C
l[:janela_ini], # D
l[:janela_fim], # E
l[:tempo], # F
l[:anotacoes].to_s, # G
(lat.nil? ? '' : lat), # H
(lng.nil? ? '' : lng), # I
l[:referencia].to_s, # J
'', '', # K, L
l[:contato].to_s, # M
l[:telefone].to_s, # N
*VAZIAS_FINAIS # O..Z
]
tipos = [
:string, :string, :integer, :string, :string, :integer, :string,
(lat.nil? ? :string : :float), # H
(lng.nil? ? :string : :float), # I
:string, :string, :string, :string, :string,
*Array.new(VAZIAS_FINAIS.size, :string)
]
[valores, tipos]
end
end
end