Por qué REF en lugar de USD
Venezuela tiene una economía dolarizada informal — los negocios manejan dólares pero de forma mixta con bolívares. Mostrar precios como ”$” puede confundir porque:
- No todos los clientes pagan en efectivo USD
- La tasa BCV cambia diario
- Algunos negocios prefieren mostrar solo bolívares
Por eso SYNTIweb usa REF (precio de referencia) como símbolo neutral.
El dueño puede personalizar el símbolo desde el dashboard (Diseño → Moneda). Algunos usan ”$”, “USD”, “Precio”, etc.
Los 4 modos de visualización
El dueño elige cómo mostrar precios desde Dashboard → Diseño → Moneda:
1. Solo REF (reference_only)
Muestra únicamente el precio en moneda de referencia.
2. Solo Bolívares (bolivares_only)
Convierte automáticamente con la tasa BCV del día.
Pizza Margarita
292.00 Bs.
3. Toggle REF/Bs (both_toggle)
Muestra un botón que permite al cliente cambiar entre REF y Bs.
Pizza Margarita
8.00 REF [🔄 Ver en Bs]
Al tocar el botón → 292.00 Bs.
4. Ocultar precios (hidden)
No muestra ningún precio. Útil para negocios que cotizan por WhatsApp.
Configuración en el backend
// DashboardController.php:893-944
public function updateCurrencyConfig(Request $request, int $tenantId): JsonResponse
{
$displayMode = $request->input('display_mode', 'reference_only');
$symbol = $request->input('symbol', 'REF');
// Mapear display_mode a flags booleanos
$showReference = in_array($displayMode, ['reference_only', 'both_toggle', 'euro_toggle']);
$showBolivares = in_array($displayMode, ['bolivares_only', 'both_toggle', 'euro_toggle']);
$showEuro = $displayMode === 'euro_toggle';
$hidePrice = $displayMode === 'hidden';
$hasToggle = in_array($displayMode, ['both_toggle', 'euro_toggle']);
$settings['engine_settings']['currency']['display']['show_reference'] = $showReference;
$settings['engine_settings']['currency']['display']['show_bolivares'] = $showBolivares;
$settings['engine_settings']['currency']['display']['show_euro'] = $showEuro;
$settings['engine_settings']['currency']['display']['hide_price'] = $hidePrice;
$settings['engine_settings']['currency']['display']['has_toggle'] = $hasToggle;
$settings['engine_settings']['currency']['display']['symbols']['reference'] = $symbol;
$settings['engine_settings']['currency']['display']['saved_display_mode'] = $displayMode;
$tenant->settings = $settings;
$tenant->save();
}
Tasa BCV automática
SYNTIweb actualiza la tasa del dólar y el euro cada hora desde la API de DolarToday/BCV.
Servicio DollarRateService
// DollarRateService.php:35-40
private const API_URL = 'https://ve.dolarapi.com/v1/dolares/oficial';
private const EURO_API_URL = 'https://ve.dolarapi.com/v1/euros/oficial';
private const CACHE_TTL = 3600; // 1 hora
Fetch y almacenamiento
// DollarRateService.php:130-236
public function fetchAndStore(): array
{
$response = Http::timeout(10)->acceptJson()->get(self::API_URL);
if (!$response->successful()) {
return ['success' => false, 'message' => "API returned status {$response->status()}"];
}
$data = $response->json();
$newRate = (float) $data['promedio'];
// Validar que la tasa no haya cambiado más de 10% (protección contra datos erróneos)
$previousRate = $this->getCurrentRate();
if ($previousRate !== null) {
$changePercent = abs(($newRate - $previousRate) / $previousRate) * 100;
if ($changePercent > 10.0) {
Log::warning('DollarRateService: Unusual rate change detected', [
'previous_rate' => $previousRate,
'new_rate' => $newRate,
'change_percent' => round($changePercent, 2)
]);
}
}
// Desactivar tasas anteriores
DollarRate::where('currency_type', 'USD')
->where('is_active', true)
->update(['is_active' => false, 'effective_until' => Carbon::now()]);
// Crear nuevo registro
DollarRate::create([
'rate' => $newRate,
'source' => 'dolarapi',
'currency_type' => 'USD',
'effective_from' => Carbon::now(),
'is_active' => true
]);
Cache::forget('dollar_rate_current');
}
Propagación a todos los tenants
// DollarRateService.php:394-489
public function propagateRateToTenants(?float $rate = null): array
{
$rate ??= $this->getCurrentRate();
$tenants = Tenant::where('status', 'active')->get();
$updatedCount = 0;
foreach ($tenants as $tenant) {
$settings = $tenant->settings ?? [];
$autoUpdate = data_get($settings, 'engine_settings.currency.auto_update', true);
if (!$autoUpdate) {
continue; // El tenant desactivó actualizaciones automáticas
}
data_set($settings, 'engine_settings.currency.exchange_rate', $rate);
data_set($settings, 'engine_settings.currency.source', 'dolarapi');
data_set($settings, 'engine_settings.currency.last_update', now()->toDateString());
$tenant->settings = $settings;
$tenant->save();
$updatedCount++;
}
return ['success' => true, 'updated_count' => $updatedCount];
}
Los tenants pueden desactivar actualizaciones automáticas si prefieren fijar una tasa manualmente (por ejemplo, casas de cambio).
Cache de tasas
// DollarRateService.php:58-87
public function getCurrentRate(): float
{
return Cache::remember('dollar_rate_current', 3600, function (): float {
$rate = DollarRate::where('currency_type', 'USD')
->where('is_active', true)
->orderByDesc('effective_from')
->first();
if ($rate === null) {
Log::warning('No active USD rate found, using fallback');
return 36.50; // Fallback
}
return (float) $rate->rate;
});
}
La tasa se cachea por 1 hora para reducir queries a la base de datos.
Historial de tasas
// DollarRateService.php:497-525
public function getHistoricalRates(int $days = 30): array
{
$rates = DollarRate::where('effective_from', '>=', Carbon::now()->subDays($days))
->orderByDesc('effective_from')
->get();
return $rates->map(function (DollarRate $rate): array {
return [
'rate' => (float) $rate->rate,
'source' => $rate->source,
'date' => $rate->effective_from->toDateTimeString(),
'is_active' => $rate->is_active
];
})->toArray();
}
El dashboard puede mostrar un gráfico de evolución de la tasa en los últimos 30 días usando este método.
Comando Artisan
La actualización automática se ejecuta vía cron cada hora:
Este comando llama a DollarRateService::fetchAndPropagateAll() que actualiza USD, EUR y propaga a todos los tenants.
Modo Euro (euro_toggle)
El Plan Anual de SYNTIfood y SYNTIcat incluye soporte para mostrar precios en Euros además de REF y Bs.
// DollarRateService.php:244-314
public function fetchAndStoreEuro(): array
{
$response = Http::timeout(10)->get(self::EURO_API_URL);
$newRate = (float) $response->json()['promedio'];
DollarRate::where('currency_type', 'EUR')
->where('is_active', true)
->update(['is_active' => false, 'effective_until' => Carbon::now()]);
DollarRate::create([
'rate' => $newRate,
'source' => 'dolarapi',
'currency_type' => 'EUR',
'effective_from' => Carbon::now(),
'is_active' => true
]);
}