# app/models/consolidacao_veiculo.rb # Unidade de FECHAMENTO por veículo dentro de uma consolidação. Espelha os padrões # de ConsolidacaoMotorista (arquivamento reversível + valor_total recalculado dos # pilares), mas o que ela controla é o estado "fechado/consolidado" de cada carro. # O PAGAMENTO continua no motorista (ConsolidacaoMotorista) — ver [[consolidacao]]. class ConsolidacaoVeiculo < ApplicationRecord # Nome da tabela fixado (mesmo motivo dos outros: inflector pt-BR). self.table_name = 'consolidacao_veiculos' # Bucket dos lançamentos sem veículo (termo/apontamento manual legado). Igual ao # sentinel usado no controller e na migração — NULL quebraria o índice único. SEM_VEICULO = '(sem veículo)' belongs_to :consolidacao belongs_to :fechador, class_name: 'User', foreign_key: :fechado_por, optional: true belongs_to :arquivador, class_name: 'User', foreign_key: :arquivado_por, optional: true validates :motorista_nome, presence: true validates :vehicle, presence: true validates :vehicle, uniqueness: { scope: %i[consolidacao_id motorista_nome] } before_save :recalcular_valor # ── Fechamento (consolidação do veículo, reversível) ──────── scope :fechados, -> { where.not(fechado_em: nil) } scope :abertos, -> { where(fechado_em: nil) } # ── Arquivamento (reversível) — sai do gate, mantém os dados ─ scope :ativos, -> { where(arquivado_em: nil) } scope :arquivados, -> { where.not(arquivado_em: nil) } def fechado? fechado_em.present? end def arquivado? arquivado_em.present? end # update_columns: não dispara before_save (o valor já está correto no fechamento). def fechar!(user) update_columns(fechado_em: Time.current, fechado_por: user&.id, updated_at: Time.current) end def reabrir!(_user) update_columns(fechado_em: nil, fechado_por: nil, updated_at: Time.current) end def arquivar!(user) update_columns(arquivado_em: Time.current, arquivado_por: user&.id, updated_at: Time.current) end def desarquivar!(_user) update_columns(arquivado_em: nil, arquivado_por: nil, updated_at: Time.current) end # Soma dos pilares deste (motorista + veículo). Mesma regra do ConsolidacaoMotorista: # o valor_aplicado capturado na classificação é a fonte da verdade e o desconto # subtrai. O bucket "(sem veículo)" casa os pilares com vehicle NULL/vazio. def recalcular_valor self.valor_total = escopo_entregas .sum { |e| e.desconto? ? -e.valor_aplicado : e.valor_aplicado } end private def escopo_entregas rel = consolidacao.consolidacao_entregas.where(motorista_nome: motorista_nome) vehicle == SEM_VEICULO ? rel.where(vehicle: [nil, '']) : rel.where(vehicle: vehicle) end end