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>
94 lines
2.8 KiB
Ruby
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
|