138 lines
6.9 KiB
Ruby
138 lines
6.9 KiB
Ruby
require 'rails_helper'
|
|
|
|
# Trava o EIXO DE DATA do espelho de rastreio.
|
|
#
|
|
# O sistema tem dois eixos e eles NÃO são intercambiáveis:
|
|
# planned_date → dia planejado da visita (scope :no_periodo)
|
|
# checkout → dia REAL da entrega (scope :no_periodo_checkout)
|
|
#
|
|
# A elegibilidade da consolidação — o que vira dinheiro — é sempre pelo
|
|
# `checkout`. Um filtro que use `planned_date` some com entrega elegível na
|
|
# virada de mês e o fechamento não bate. Foi exatamente o bug do seletor de
|
|
# veículos da nova consolidação (Entrega.veiculos_de_motoristas usava
|
|
# :no_periodo), por isso o caso da virada está fixado aqui.
|
|
#
|
|
# A tabela do espelho é EXTERNA (sem migration, sem factory) — quem a cria e
|
|
# derruba no banco de teste é o harness em spec/support/espelho_rastreio.rb, que
|
|
# também expõe o helper `semear`.
|
|
RSpec.describe Entrega, type: :model do
|
|
include_context 'espelho de rastreio'
|
|
|
|
describe '.veiculos_de_motoristas' do
|
|
# Período de fechamento usado em todos os exemplos.
|
|
let(:inicio) { Date.new(2026, 8, 1) }
|
|
let(:fim) { Date.new(2026, 8, 14) }
|
|
|
|
it 'inclui o veículo de entrega PLANEJADA fora do período mas ENTREGUE dentro' do
|
|
# A regressão: planejada em 31/07, entregue em 01/08. É elegível (checkout
|
|
# dentro), logo o veículo TEM que aparecer — senão não há como marcá-lo no
|
|
# filtro e a entrega sai do fechamento sem aviso.
|
|
semear([{ driver: 'JOSE DA SILVA', vehicle: 'GADE_001', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 7, 31, 8, 0),
|
|
checkout: Time.zone.local(2026, 8, 1, 9, 30) }])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to eq(['GADE_001'])
|
|
end
|
|
|
|
it 'exclui o veículo de entrega PLANEJADA dentro do período mas ENTREGUE fora' do
|
|
# O espelho do caso acima: planejada em 14/08, entregue em 20/08. Não é
|
|
# elegível neste fechamento, então o veículo não pode aparecer.
|
|
semear([{ driver: 'JOSE DA SILVA', vehicle: 'GADE_002', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 8, 14, 8, 0),
|
|
checkout: Time.zone.local(2026, 8, 20, 9, 30) }])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to be_empty
|
|
end
|
|
|
|
it 'inclui o último dia do período inteiro (checkout às 23h de 14/08)' do
|
|
# no_periodo_checkout usa `< fim + 1`; um `<= fim` cortaria o último dia.
|
|
semear([{ driver: 'JOSE DA SILVA', vehicle: 'GADE_003', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 8, 14, 7, 0),
|
|
checkout: Time.zone.local(2026, 8, 14, 23, 40) }])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to eq(['GADE_003'])
|
|
end
|
|
|
|
it 'inclui veículo de INSUCESSO (failed conta como atendida)' do
|
|
# Atendida = o motorista foi ao local, com sucesso OU insucesso. Insucesso
|
|
# é elegível e precisa ser classificado no wizard.
|
|
semear([{ driver: 'JOSE DA SILVA', vehicle: 'GADE_004', status: 'failed',
|
|
planned_date: Time.zone.local(2026, 8, 5, 8, 0),
|
|
checkout: Time.zone.local(2026, 8, 5, 16, 0) }])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to eq(['GADE_004'])
|
|
end
|
|
|
|
it 'exclui veículo que só tem entrega PENDENTE (sem checkout) no período' do
|
|
# Pendente não é elegível; listar o veículo só sujaria o filtro com um
|
|
# carro de zero elegíveis.
|
|
semear([{ driver: 'JOSE DA SILVA', vehicle: 'GADE_005', status: 'pending',
|
|
planned_date: Time.zone.local(2026, 8, 6, 8, 0), checkout: nil }])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to be_empty
|
|
end
|
|
|
|
it 'não vaza veículo de outro motorista' do
|
|
semear([
|
|
{ driver: 'JOSE DA SILVA', vehicle: 'GADE_006', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 8, 4, 8, 0), checkout: Time.zone.local(2026, 8, 4, 10, 0) },
|
|
{ driver: 'MARIA SOUZA', vehicle: 'GADE_007', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 8, 4, 8, 0), checkout: Time.zone.local(2026, 8, 4, 11, 0) }
|
|
])
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim))
|
|
.to eq(['GADE_006'])
|
|
end
|
|
|
|
# A invariante que realmente protege o financeiro: o seletor de veículos do
|
|
# formulário e o conjunto elegível da consolidação têm que enxergar o MESMO
|
|
# recorte. Se alguém mexer no eixo de data de um dos dois, este exemplo cai.
|
|
it 'lista exatamente os veículos que têm entrega elegível no período' do
|
|
semear([
|
|
# elegíveis (checkout dentro)
|
|
{ driver: 'JOSE DA SILVA', vehicle: 'GADE_010', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 7, 30, 8, 0), checkout: Time.zone.local(2026, 8, 2, 10, 0) },
|
|
{ driver: 'JOSE DA SILVA', vehicle: 'GADE_011', status: 'failed',
|
|
planned_date: Time.zone.local(2026, 8, 9, 8, 0), checkout: Time.zone.local(2026, 8, 9, 10, 0) },
|
|
# não elegíveis
|
|
{ driver: 'JOSE DA SILVA', vehicle: 'GADE_012', status: 'completed',
|
|
planned_date: Time.zone.local(2026, 8, 10, 8, 0), checkout: Time.zone.local(2026, 9, 1, 10, 0) },
|
|
{ driver: 'JOSE DA SILVA', vehicle: 'GADE_013', status: 'pending',
|
|
planned_date: Time.zone.local(2026, 8, 11, 8, 0), checkout: nil }
|
|
])
|
|
|
|
elegiveis = described_class.atendidas.da_conta_gade
|
|
.no_periodo_checkout(inicio, fim)
|
|
.do_motorista('JOSE DA SILVA')
|
|
.distinct.pluck(:vehicle).compact.sort
|
|
|
|
expect(described_class.veiculos_de_motoristas(['JOSE DA SILVA'], inicio: inicio, fim: fim).sort)
|
|
.to eq(elegiveis)
|
|
expect(elegiveis).to eq(%w[GADE_010 GADE_011])
|
|
end
|
|
|
|
it 'devolve vazio sem motorista informado (não toca o banco)' do
|
|
expect(described_class.veiculos_de_motoristas([], inicio: inicio, fim: fim)).to eq([])
|
|
expect(described_class.veiculos_de_motoristas(nil, inicio: inicio, fim: fim)).to eq([])
|
|
end
|
|
end
|
|
|
|
# Os dois scopes são a origem da confusão — vale fixar que cada um olha para a
|
|
# coluna que promete, para nenhum refactor trocar um pelo outro em silêncio.
|
|
describe 'eixos de período' do
|
|
it ':no_periodo filtra por planned_date e :no_periodo_checkout por checkout' do
|
|
expect(described_class.no_periodo(Date.new(2026, 8, 1), Date.new(2026, 8, 14)).to_sql)
|
|
.to include('planned_date')
|
|
|
|
sql_checkout = described_class.no_periodo_checkout(Date.new(2026, 8, 1), Date.new(2026, 8, 14)).to_sql
|
|
expect(sql_checkout).to include('checkout')
|
|
expect(sql_checkout).not_to include('planned_date')
|
|
end
|
|
end
|
|
end
|