# app/models/user.rb class User < ApplicationRecord devise :database_authenticatable, :recoverable, :rememberable, :validatable # ── Roles ────────────────────────────────────────────────── # externo: acesso somente-leitura ao dashboard (ex.: gerências de outras áreas # que só precisam visualizar os indicadores). Login normal por e-mail/senha. enum role: { admin: 0, gerente: 1, operador: 2, motorista: 3, externo: 4 } ROLES_LABEL = { 'admin' => 'Administrador', 'gerente' => 'Gerente', 'operador' => 'Operador', 'motorista' => 'Motorista', 'externo' => 'Externo (só dashboard)' }.freeze # ── Validações ────────────────────────────────────────────── validates :nome, presence: true, length: { minimum: 3 } validates :role, presence: true validates :pin_code, length: { is: 4 }, numericality: { only_integer: true }, allow_nil: true, if: :motorista? validate :pin_unico_para_motoristas, if: :motorista? # E-mail em branco vira NULL: evita colisão no índice único quando vários # motoristas são criados sem e-mail (no Postgres NULLs são distintos entre si). before_validation :normalizar_email_em_branco # Motoristas podem não ter e-mail (login via PIN) def email_required? !motorista? end def password_required? !motorista? ? super : false end # ── Escopos ───────────────────────────────────────────────── scope :ativos, -> { where(ativo: true) } scope :por_nome, -> { order(:nome) } scope :nao_motoristas, -> { where.not(role: :motorista) } # ── Helpers ───────────────────────────────────────────────── def nome_display nome.presence || email end def role_label ROLES_LABEL[role] || role.humanize end def pode_ver_config? admin? || gerente? end def pode_consolidar? admin? || gerente? || operador? end # Token de login rápido via QR Code (holerite) — Fase 7/8 def regenerar_login_token! update!(login_token: SecureRandom.urlsafe_base64(24)) end private def normalizar_email_em_branco self.email = nil if email.blank? end def pin_unico_para_motoristas return if pin_code.blank? conflito = User.motorista.where(pin_code: pin_code).where.not(id: id) errors.add(:pin_code, 'já está em uso por outro motorista') if conflito.exists? end end