teste #7

Merged
victor merged 15 commits from teste into main 2026-07-15 19:30:26 -03:00
13 changed files with 158 additions and 62 deletions
Showing only changes of commit 5af51534be - Show all commits

View File

@@ -64,7 +64,8 @@ class ConsolidacaoEntregasController < ApplicationController
bonus: Configuracao.preco_bonus,
desconto: Configuracao.preco_desconto,
extraordinaria: Configuracao.preco_extraordinaria,
termo: Configuracao.preco_termo
termo: Configuracao.preco_termo,
termo_especial: Configuracao.preco_termo_especial
}
end
@@ -267,25 +268,38 @@ class ConsolidacaoEntregasController < ApplicationController
end
# POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar_termo
# ENTREGA DE TERMO — lança um lote de N termos para o motorista. Não há NF nem
# código de rastreio: só a quantidade, a um preço fixo configurável.
# ENTREGA DE TERMO — lança lotes de termos para o motorista. Não há NF nem
# código de rastreio: só as quantidades (normal e especial), cada tipo a um
# preço fixo configurável.
def apontar_termo
authorize @consolidacao, :update?
motorista = @consolidacao.adicionar_termos!(
motorista: params[:motorista].to_s.strip,
quantidade: params[:quantidade],
vehicle: params[:vehicle], # veículo escolhido no lançamento (Q3)
user: current_user
)
qtd_normal = params[:quantidade].to_i
qtd_especial = params[:quantidade_especial].to_i
raise ArgumentError, 'Informe ao menos um termo (normal ou especial).' if qtd_normal < 1 && qtd_especial < 1
motorista = nil
ActiveRecord::Base.transaction do
{ 'termo' => qtd_normal, 'termo_especial' => qtd_especial }.each do |tipo, qtd|
next if qtd < 1
motorista = @consolidacao.adicionar_termos!(
motorista: params[:motorista].to_s.strip,
quantidade: qtd,
tipo: tipo,
vehicle: params[:vehicle], # veículo escolhido no lançamento (Q3)
user: current_user
)
end
end
auditar!(:editar, @consolidacao,
dados_novos: { entrega_termo: params[:quantidade].to_i, motorista: motorista })
dados_novos: { entrega_termo: qtd_normal, entrega_termo_especial: qtd_especial, motorista: motorista })
respond_to do |format|
format.json { render json: resumo_json(motorista).merge(motorista: motorista) }
format.html do
redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista),
notice: "#{params[:quantidade].to_i} entrega(s) de termo adicionada(s) para #{motorista}."
notice: "#{qtd_normal + qtd_especial} entrega(s) de termo adicionada(s) para #{motorista}."
end
end
rescue ArgumentError => e
@@ -534,8 +548,9 @@ class ConsolidacaoEntregasController < ApplicationController
# Conta lançamentos manuais como ENTREGAS: cada termo vale `quantidade` (lote
# numa só linha); os demais apontamentos manuais valem 1 por nota (tracking).
def contar_manuais(rel_manual)
rel_manual.termo.sum(:quantidade) +
rel_manual.where.not(tipo: ConsolidacaoEntrega.tipos[:termo]).distinct.count(:tracking_id)
tipos_termo = ConsolidacaoEntrega.tipos.values_at(:termo, :termo_especial)
rel_manual.where(tipo: tipos_termo).sum(:quantidade) +
rel_manual.where.not(tipo: tipos_termo).distinct.count(:tracking_id)
end
# `vehicles` (opcional, string ou array): quando presente, TODO o resumo

View File

@@ -1,17 +1,18 @@
// app/javascript/controllers/apontamento_termo_controller.js
// Stimulus — Entrega de termo. Lança um lote de N termos para o motorista da tela.
// Entrega sem NF nem código de rastreio: só a quantidade (stepper N +), a um
// preço fixo configurável.
// Stimulus — Entrega de termo. Lança lotes de termos (normal e/ou especial) para
// o motorista da tela. Entrega sem NF nem código de rastreio: só as quantidades
// (um stepper N + por tipo), cada tipo a um preço fixo configurável.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["modal", "quantidade", "erro", "veiculo"]
static targets = ["modal", "quantidade", "quantidadeEspecial", "erro", "veiculo"]
static values = { apontarTermoUrl: String, motorista: String }
abrir() {
this.erroTarget.classList.add("hidden")
this.quantidadeTarget.value = "1"
this.quantidadeEspecialTarget.value = "0"
this.modalTarget.classList.remove("hidden")
}
@@ -24,26 +25,39 @@ export default class extends Controller {
if (event.target === this.modalTarget) this.fechar()
}
// Stepper
// Steppers (mínimo 0 em ambos; o envio exige ao menos 1 no total)
mais() {
this.quantidadeTarget.value = String(this.qtdAtual() + 1)
this.quantidadeTarget.value = String(this.qtdAtual(this.quantidadeTarget) + 1)
}
menos() {
this.quantidadeTarget.value = String(Math.max(1, this.qtdAtual() - 1))
this.quantidadeTarget.value = String(Math.max(0, this.qtdAtual(this.quantidadeTarget) - 1))
}
qtdAtual() {
const n = parseInt(this.quantidadeTarget.value, 10)
return Number.isFinite(n) && n > 0 ? n : 1
maisEspecial() {
this.quantidadeEspecialTarget.value = String(this.qtdAtual(this.quantidadeEspecialTarget) + 1)
}
menosEspecial() {
this.quantidadeEspecialTarget.value = String(Math.max(0, this.qtdAtual(this.quantidadeEspecialTarget) - 1))
}
qtdAtual(alvo) {
const n = parseInt(alvo.value, 10)
return Number.isFinite(n) && n > 0 ? n : 0
}
async adicionar(event) {
event?.preventDefault()
const quantidade = this.qtdAtual()
const motorista = this.motoristaValue.trim()
const quantidade = this.qtdAtual(this.quantidadeTarget)
const quantidadeEspecial = this.qtdAtual(this.quantidadeEspecialTarget)
const motorista = this.motoristaValue.trim()
if (!motorista) { this.mostrarErro("Motorista não identificado."); return }
if (quantidade + quantidadeEspecial < 1) {
this.mostrarErro("Informe ao menos um termo (normal ou especial).")
return
}
const btn = event.currentTarget
btn.disabled = true
@@ -56,9 +70,10 @@ export default class extends Controller {
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
motorista: motorista,
quantidade: quantidade,
vehicle: this.hasVeiculoTarget ? this.veiculoTarget.value : ""
motorista: motorista,
quantidade: quantidade,
quantidade_especial: quantidadeEspecial,
vehicle: this.hasVeiculoTarget ? this.veiculoTarget.value : ""
})
})
const data = await resp.json()

View File

@@ -12,7 +12,7 @@ export default class extends Controller {
"consolidarBtn", "consolidarHint",
"contadorClassificadas", "contadorTotal", "contadorManuais", "manuaisInfo",
"qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "qtdExtraordinaria",
"qtdTermo", "qtdManuais", "valorTotal"
"qtdTermo", "qtdTermoEspecial", "qtdManuais", "valorTotal"
]
static values = {
@@ -187,6 +187,8 @@ export default class extends Controller {
this.qtdExtraordinariaTarget.textContent = porTipo["extraordinaria"] || 0
if (this.hasQtdTermoTarget)
this.qtdTermoTarget.textContent = porTipo["termo"] || 0
if (this.hasQtdTermoEspecialTarget)
this.qtdTermoEspecialTarget.textContent = porTipo["termo_especial"] || 0
const valor = dados.valor_total || 0
this.valorTotalTarget.textContent =

View File

@@ -10,6 +10,7 @@ class Configuracao < ApplicationRecord
preco_desconto
preco_extraordinaria
preco_termo
preco_termo_especial
notificacao_whatsapp
notificacao_email
empresa_nome
@@ -23,6 +24,7 @@ class Configuracao < ApplicationRecord
preco_desconto
preco_extraordinaria
preco_termo
preco_termo_especial
].freeze
validates :chave, presence: true, inclusion: { in: CHAVES }, uniqueness: true
@@ -58,6 +60,10 @@ class Configuracao < ApplicationRecord
valor('preco_termo').to_f
end
def self.preco_termo_especial
valor('preco_termo_especial').to_f
end
# Mapa usado pelo DashboardController (Fase 3)
def self.mapa_de_precos
{
@@ -66,7 +72,8 @@ class Configuracao < ApplicationRecord
bonus: preco_bonus,
desconto: preco_desconto,
extraordinaria: preco_extraordinaria,
termo: preco_termo
termo: preco_termo,
termo_especial: preco_termo_especial
}
end
@@ -77,6 +84,7 @@ class Configuracao < ApplicationRecord
'preco_desconto' => 'Desconto',
'preco_extraordinaria' => 'Entrega Extraordinária',
'preco_termo' => 'Entrega de Termo',
'preco_termo_especial' => 'Entrega de Termo Especial',
'notificacao_whatsapp' => 'Notificação WhatsApp',
'notificacao_email' => 'Notificação E-mail',
'empresa_nome' => 'Nome da Empresa'
@@ -89,6 +97,7 @@ class Configuracao < ApplicationRecord
'preco_desconto' => '⚠️',
'preco_extraordinaria' => '✨',
'preco_termo' => '📄',
'preco_termo_especial' => '📋',
'notificacao_whatsapp' => '💬',
'notificacao_email' => '✉️',
'empresa_nome' => '🏢'

View File

@@ -339,10 +339,15 @@ class Consolidacao < ApplicationRecord
# para atrelar (só motorista + quantidade, a um preço fixo configurável). Fica
# numa única linha (manual: true): quantidade guarda o N e valor_aplicado guarda
# o total do lote, então recalcular_motorista! e demais somas seguem inalteradas.
def adicionar_termos!(motorista:, quantidade:, user:, vehicle: nil)
# `tipo` distingue termo normal ('termo') de termo especial ('termo_especial'),
# cada um com seu preço configurável.
def adicionar_termos!(motorista:, quantidade:, user:, vehicle: nil, tipo: 'termo')
nome = motorista.to_s.strip
raise ArgumentError, 'Informe o motorista do termo.' if nome.blank?
tipo = tipo.to_s
raise ArgumentError, 'Tipo de termo inválido.' unless %w[termo termo_especial].include?(tipo)
qtd = quantidade.to_i
raise ArgumentError, 'Informe uma quantidade válida de termos.' if qtd < 1
@@ -351,10 +356,10 @@ class Consolidacao < ApplicationRecord
consolidacao_entregas.create!(
tracking_id: "TERMO-#{SecureRandom.uuid}", # sintético: não há Entrega/NF
tipo: 'termo',
tipo: tipo,
motorista_nome: nome,
quantidade: qtd,
valor_aplicado: qtd * ConsolidacaoEntrega.valor_para('termo'), # total do lote
valor_aplicado: qtd * ConsolidacaoEntrega.valor_para(tipo), # total do lote
manual: true,
vehicle: vehicle.presence, # veículo escolhido no lançamento (Q3)
created_by: user&.id

View File

@@ -15,7 +15,8 @@ class ConsolidacaoEntrega < ApplicationRecord
bonus: 2,
desconto: 3,
extraordinaria: 4,
termo: 5
termo: 5,
termo_especial: 6
}
TIPO_CORES = {
@@ -24,7 +25,8 @@ class ConsolidacaoEntrega < ApplicationRecord
'bonus' => { bg: 'bg-white border border-orange-500', text: 'text-black', label: 'Bônus' },
'desconto' => { bg: 'bg-gray-900', text: 'text-white', label: 'Desconto' },
'extraordinaria' => { bg: 'bg-purple-600', text: 'text-white', label: 'Entrega Extraordinária' },
'termo' => { bg: 'bg-blue-600', text: 'text-white', label: 'Entrega de Termo' }
'termo' => { bg: 'bg-blue-600', text: 'text-white', label: 'Entrega de Termo' },
'termo_especial' => { bg: 'bg-cyan-600', text: 'text-white', label: 'Entrega de Termo Especial' }
}.freeze
# Uma entrega pode ter vários pilares (Normal + Bônus + Retirada…),
@@ -54,6 +56,7 @@ class ConsolidacaoEntrega < ApplicationRecord
when 'desconto' then Configuracao.preco_desconto
when 'extraordinaria' then Configuracao.preco_extraordinaria
when 'termo' then Configuracao.preco_termo
when 'termo_especial' then Configuracao.preco_termo_especial
else 0
end
end

View File

@@ -37,7 +37,7 @@ module Pdf
original&.vehicle.presence || (e.manual? ? 'manual' : '—'),
original ? "NF #{original.numero_nf}" : (e.nf_manual.present? ? "NF #{e.nf_manual}" : '—'),
original ? original.address.to_s : (e.obs_manual.presence || '—'),
e.termo? && e.quantidade > 1 ? "#{e.tipo_cor[:label]} ×#{e.quantidade}" : e.tipo_cor[:label],
(e.termo? || e.termo_especial?) && e.quantidade > 1 ? "#{e.tipo_cor[:label]} ×#{e.quantidade}" : e.tipo_cor[:label],
(e.desconto? ? '-' : '') + moeda(e.valor_aplicado)
]
end

View File

@@ -12,14 +12,15 @@
</div>
<%# Resumo por tipo %>
<div class="grid grid-cols-2 md:grid-cols-6 gap-3 mb-6">
<div class="grid grid-cols-2 md:grid-cols-7 gap-3 mb-6">
<% [
['entrega_normal', 'Normal', 'bg-orange-500 text-black'],
['retirada', 'Retirada', 'bg-orange-800 text-white'],
['bonus', 'Bônus', 'bg-white text-black'],
['desconto', 'Desconto', 'bg-black text-white border border-gray-700'],
['extraordinaria', 'Extraordinária', 'bg-purple-600 text-white'],
['termo', 'Termo', 'bg-blue-600 text-white']
['termo', 'Termo', 'bg-blue-600 text-white'],
['termo_especial', 'Termo Especial', 'bg-cyan-600 text-white']
].each do |tipo, label, cores| %>
<div class="<%= cores %> rounded-xl p-4 text-center">
<p class="font-black text-3xl"><%= @resumo[tipo] || 0 %></p>
@@ -67,7 +68,7 @@
<td class="px-4 py-3 text-gray-300 text-xs"><%= c.entrega_original&.address.presence || endereco_manual.presence || '—' %></td>
<td class="px-4 py-3">
<span class="<%= c.tipo_cor[:bg] %> <%= c.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold">
<%= c.tipo_cor[:label] %><% if c.termo? && c.quantidade > 1 %> ×<%= c.quantidade %><% end %>
<%= c.tipo_cor[:label] %><% if (c.termo? || c.termo_especial?) && c.quantidade > 1 %> ×<%= c.quantidade %><% end %>
</span>
<% if c.manual? %>
<span class="ml-1 bg-orange-500/15 text-orange-400 border border-orange-500/40 px-2 py-1 rounded text-xs font-bold"> Apontamento</span>

View File

@@ -288,20 +288,14 @@
<button type="button" data-action="apontamento-termo#fechar"
class="text-gray-500 hover:text-white text-2xl leading-none">✕</button>
</div>
<p class="text-gray-400 text-sm mb-4">Entrega sem nota fiscal nem código de rastreio. Informe quantos termos foram entregues.</p>
<p class="text-gray-400 text-sm mb-4">Entrega sem nota fiscal nem código de rastreio. Informe quantos termos de cada tipo foram entregues.</p>
<p data-apontamento-termo-target="erro" class="hidden bg-red-500/10 text-red-400 border border-red-500/30 rounded-lg px-3 py-2 text-sm mb-3"></p>
<%# Motorista (contexto da tela) + preço unitário %>
<div class="flex items-center justify-between bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-4 py-3 mb-4">
<div>
<p class="text-gray-500 text-xs">Motorista</p>
<p class="text-white font-semibold">🚚 <%= @motorista %></p>
</div>
<div class="text-right">
<p class="text-gray-500 text-xs">Preço por termo</p>
<p class="text-blue-400 font-semibold"><%= moeda(Configuracao.preco_termo) %></p>
</div>
<%# Motorista (contexto da tela) %>
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg px-4 py-3 mb-4">
<p class="text-gray-500 text-xs">Motorista</p>
<p class="text-white font-semibold">🚚 <%= @motorista %></p>
</div>
<%# Veículo (Q3) — a qual carro estes termos pertencem %>
@@ -314,15 +308,32 @@
<% end %>
</select>
<%# Stepper de quantidade %>
<label class="block text-gray-400 text-sm mb-2">Quantidade de termos</label>
<div class="flex items-center justify-center gap-3 mb-5">
<button type="button" data-action="apontamento-termo#menos"
class="w-12 h-12 rounded-lg bg-[#0a0a0a] border border-[#2a2a2a] hover:border-blue-500 text-white text-2xl font-bold leading-none"></button>
<input data-apontamento-termo-target="quantidade" type="text" inputmode="numeric" value="1" readonly
class="w-24 text-center bg-[#0a0a0a] border border-[#2a2a2a] text-white text-2xl font-bold rounded-lg py-2.5 focus:outline-none">
<button type="button" data-action="apontamento-termo#mais"
class="w-12 h-12 rounded-lg bg-[#0a0a0a] border border-[#2a2a2a] hover:border-blue-500 text-white text-2xl font-bold leading-none">+</button>
<%# Steppers de quantidade — termo normal e termo especial %>
<div class="grid grid-cols-2 gap-3 mb-5">
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3">
<p class="text-white text-sm font-semibold text-center">📄 Termo Normal</p>
<p class="text-blue-400 text-xs text-center mb-2"><%= moeda(@precos[:termo]) %> cada</p>
<div class="flex items-center justify-center gap-2">
<button type="button" data-action="apontamento-termo#menos"
class="w-10 h-10 rounded-lg bg-[#1a1a1a] border border-[#2a2a2a] hover:border-blue-500 text-white text-xl font-bold leading-none"></button>
<input data-apontamento-termo-target="quantidade" type="text" inputmode="numeric" value="1" readonly
class="w-14 text-center bg-[#1a1a1a] border border-[#2a2a2a] text-white text-xl font-bold rounded-lg py-2 focus:outline-none">
<button type="button" data-action="apontamento-termo#mais"
class="w-10 h-10 rounded-lg bg-[#1a1a1a] border border-[#2a2a2a] hover:border-blue-500 text-white text-xl font-bold leading-none">+</button>
</div>
</div>
<div class="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-3">
<p class="text-white text-sm font-semibold text-center">📋 Termo Especial</p>
<p class="text-cyan-400 text-xs text-center mb-2"><%= moeda(@precos[:termo_especial]) %> cada</p>
<div class="flex items-center justify-center gap-2">
<button type="button" data-action="apontamento-termo#menosEspecial"
class="w-10 h-10 rounded-lg bg-[#1a1a1a] border border-[#2a2a2a] hover:border-cyan-500 text-white text-xl font-bold leading-none"></button>
<input data-apontamento-termo-target="quantidadeEspecial" type="text" inputmode="numeric" value="0" readonly
class="w-14 text-center bg-[#1a1a1a] border border-[#2a2a2a] text-white text-xl font-bold rounded-lg py-2 focus:outline-none">
<button type="button" data-action="apontamento-termo#maisEspecial"
class="w-10 h-10 rounded-lg bg-[#1a1a1a] border border-[#2a2a2a] hover:border-cyan-500 text-white text-xl font-bold leading-none">+</button>
</div>
</div>
</div>
<button type="button" data-action="apontamento-termo#adicionar"
@@ -437,6 +448,10 @@
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-blue-600 rounded-full"></span> Termo</span>
<span class="text-white font-bold" data-validacao-target="qtdTermo">0</span>
</div>
<div class="flex justify-between items-center">
<span class="flex items-center gap-2"><span class="w-3 h-3 bg-cyan-600 rounded-full"></span> Termo Especial</span>
<span class="text-white font-bold" data-validacao-target="qtdTermoEspecial">0</span>
</div>
</div>
<div class="border-t border-[#2a2a2a] pt-4 flex justify-between items-center text-sm">
@@ -456,6 +471,7 @@
<p>⚠️ Desconto: -<%= moeda(@precos[:desconto]) %></p>
<p>✨ Extraordinária: <%= moeda(@precos[:extraordinaria]) %> (valor por entrega)</p>
<p>📄 Termo: <%= moeda(@precos[:termo]) %></p>
<p>📋 Termo Especial: <%= moeda(@precos[:termo_especial]) %></p>
</div>
</div>
</div>

View File

@@ -171,14 +171,20 @@
</thead>
<tbody class="divide-y divide-[#2a2a2a]">
<% @apontamentos.each do |(motorista, _tracking), pilares| %>
<% eh_termo = pilares.first.termo? %>
<% eh_termo = pilares.first.termo? || pilares.first.termo_especial? %>
<% entrega = eh_termo ? nil : pilares.first.entrega_original %>
<% nf_exibida = entrega&.numero_nf || pilares.first.nf_manual %>
<% obs_manual = pilares.first.obs_manual %>
<% total = pilares.sum { |p| p.desconto? ? -p.valor_aplicado : p.valor_aplicado } %>
<tr>
<td class="px-4 py-3">
<p class="text-white font-bold"><%= eh_termo ? '📄 Entrega de termo' : "NF #{nf_exibida.presence || '—'}" %></p>
<p class="text-white font-bold"><%= if pilares.first.termo?
'📄 Entrega de termo'
elsif pilares.first.termo_especial?
'📋 Entrega de termo especial'
else
"NF #{nf_exibida.presence || '—'}"
end %></p>
<p class="text-gray-400 text-xs">
<%= motorista %><% if entrega&.vehicle.present? %> · <%= entrega.vehicle %><% end %>
<% if entrega.nil? && obs_manual.present? %> · <%= obs_manual %><% end %>
@@ -187,7 +193,7 @@
<td class="px-4 py-3">
<div class="flex flex-wrap gap-1">
<% pilares.each do |p| %>
<span class="<%= p.tipo_cor[:bg] %> <%= p.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold"><%= p.tipo_cor[:label] %><% if p.termo? && p.quantidade > 1 %> ×<%= p.quantidade %><% end %></span>
<span class="<%= p.tipo_cor[:bg] %> <%= p.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold"><%= p.tipo_cor[:label] %><% if (p.termo? || p.termo_especial?) && p.quantidade > 1 %> ×<%= p.quantidade %><% end %></span>
<% end %>
</div>
</td>

View File

@@ -0,0 +1,18 @@
# Cria a configuração de preço "Entrega de Termo Especial" em bancos já
# existentes, para que apareça na tela de Configurações. Idempotente — não
# sobrescreve se o admin já tiver definido um valor.
class AddPrecoTermoEspecialConfiguracao < ActiveRecord::Migration[7.1]
def up
return if Configuracao.exists?(chave: 'preco_termo_especial')
Configuracao.create!(
chave: 'preco_termo_especial',
valor: '0.00',
descricao: 'Valor pago por entrega de termo especial (R$)'
)
end
def down
Configuracao.where(chave: 'preco_termo_especial').delete_all
end
end

View File

@@ -30,6 +30,7 @@ configs = [
{ chave: 'preco_desconto', valor: '15.00', descricao: 'Valor descontado por problema (R$)' },
{ chave: 'preco_extraordinaria', valor: '25.00', descricao: 'Valor coringa para entregas fora do planejamento (R$)' },
{ chave: 'preco_termo', valor: '15.00', descricao: 'Valor pago por entrega de termo (R$)' },
{ chave: 'preco_termo_especial', valor: '0.00', descricao: 'Valor pago por entrega de termo especial (R$)' },
{ chave: 'notificacao_whatsapp', valor: 'false', descricao: 'Notificações via WhatsApp (true/false)' },
{ chave: 'notificacao_email', valor: 'false', descricao: 'Notificações via e-mail (true/false)' },
{ chave: 'empresa_nome', valor: 'Reem Transporte', descricao: 'Nome da empresa exibido no sistema' },

View File

@@ -30,6 +30,11 @@ RSpec.describe Configuracao, type: :model do
it 'retorna 0.0 quando a chave não existe' do
expect(Configuracao.preco_bonus).to eq(0.0)
end
it 'aceita a chave preco_termo_especial e converte em float' do
create(:configuracao, chave: 'preco_termo_especial', valor: '25.00')
expect(Configuracao.preco_termo_especial).to eq(25.0)
end
end
describe '#valor_formatado' do