Files
Reem-Notas/app/models/consolidacao.rb
victor 19a72c066f Múltiplos pilares por entrega + tela de preços só com pilares de preço
Consolidação — multi-pilar:
- Uma entrega pode ter vários pilares ao mesmo tempo (Normal + Bônus +
  Retirada…), somando os valores. Toggles viraram multi-seleção (liga/desliga
  por pilar; classificar agora faz toggle, em massa só adiciona).
- migration: unicidade passa de [consolidacao_id, tracking_id] para
  [consolidacao_id, tracking_id, tipo].
- classificadas_count/resumo contam entregas distintas (≥1 pilar).

Configurações:
- /admin/configuracoes lista só os pilares de PREÇO (empresa/notificações
  não são preço e saíram da tela).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 03:05:15 -03:00

104 lines
4.0 KiB
Ruby

# app/models/consolidacao.rb
class Consolidacao < ApplicationRecord
self.table_name = 'consolidacoes'
belongs_to :criador, class_name: 'User', foreign_key: :created_by
belongs_to :finalizador, class_name: 'User', foreign_key: :finalizado_por, optional: true
belongs_to :arquivador, class_name: 'User', foreign_key: :arquivado_por, optional: true
has_many :consolidacao_motoristas, dependent: :destroy
has_many :consolidacao_entregas, dependent: :destroy
# ── Enums ───────────────────────────────────────────────────
enum status: { rascunho: 0, finalizada: 1, arquivada: 2 }
# ── Soft delete ─────────────────────────────────────────────
scope :ativas, -> { where(deleted_at: nil) }
scope :arquivadas, -> { where.not(deleted_at: nil) }
# ── Validações ──────────────────────────────────────────────
validates :nome, presence: true
validates :data_inicio, presence: true
validates :data_fim, presence: true
validates :created_by, presence: true
validate :periodo_valido
# ── Callbacks ───────────────────────────────────────────────
before_save :recalcular_valor_total
# ── Escopos ─────────────────────────────────────────────────
scope :recentes, -> { order(created_at: :desc) }
scope :no_periodo, ->(i, f) { where('data_inicio >= ? AND data_fim <= ?', i, f) }
# ── Métodos ─────────────────────────────────────────────────
def arquivar!(user)
update!(
deleted_at: Time.current,
status: :arquivada,
arquivado_por: user&.id,
arquivado_em: Time.current
)
end
def finalizar!(user)
return false unless todas_entregas_classificadas?
update!(
status: :finalizada,
finalizado_por: user.id,
finalizado_em: Time.current
)
end
# Só está completa quando CADA motorista tem todas as suas entregas elegíveis
# classificadas. (Antes era uma soma agregada com >=, que mascarava motoristas
# pendentes quando outro tinha entregas a mais.)
def todas_entregas_classificadas?
consolidacao_motoristas.all? do |cm|
total = entregas_elegiveis_count(cm.motorista_nome)
total.zero? || classificadas_count(cm.motorista_nome) >= total
end
end
def percentual_classificado
return 0 if total_entregas_estimadas.zero?
((consolidacao_entregas.count.to_f / total_entregas_estimadas) * 100).round
end
def total_entregas_estimadas
@total_entregas_estimadas ||= consolidacao_motoristas.sum do |cm|
entregas_elegiveis_count(cm.motorista_nome)
end
end
# Entregas pagas elegíveis para um motorista no período, respeitando o filtro
# de veículos da consolidação (quando houver). Fonte única usada pelo wizard,
# pela revisão e pelo gate de finalização.
def entregas_elegiveis_count(motorista)
Entrega.contar_pagas(
inicio: data_inicio,
fim: data_fim,
motorista: motorista,
vehicle: vehicle_ids.presence
)
end
# Entrega conta como classificada se tiver ≥1 pilar (distinct por tracking_id,
# já que agora pode haver vários pilares por entrega).
def classificadas_count(motorista)
consolidacao_entregas.where(motorista_nome: motorista).distinct.count(:tracking_id)
end
private
def periodo_valido
return unless data_inicio && data_fim
errors.add(:data_fim, 'deve ser após a data de início') if data_fim < data_inicio
end
def recalcular_valor_total
self.valor_total = consolidacao_motoristas.sum(:valor_total)
end
end