Files
2026-10-20 13:16:38 +09:00

114 lines
4.2 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/db.php';
const SERUBI_PRODUCTS = ['giro' => 'Giro Umum', 'simanja' => 'Tabungan SIMANJA', 'simpeda' => 'Tabungan SIMPEDA'];
const SERUBI_GROUPS = [
'dana' => ['Struktur dana', 'Bagaimana dana nasabah dibagi sebelum menghasilkan pendapatan.'],
'bunga' => ['Suku bunga pendapatan', 'Rate yang menghasilkan pendapatan bank dari dana kelolaan.'],
'biaya' => ['Biaya', 'Biaya yang mengurangi spread sebelum dibagi ke nasabah.'],
'pajak' => ['Pajak', 'Tarif pajak atas bunga dan cashback.'],
'konvensi' => ['Konvensi perhitungan', 'Hari bunga, pinalti, dan batas minimal.'],
];
/** Raw settings rows keyed by skey. */
function settings_rows(): array
{
$rows = db()->query('SELECT * FROM settings ORDER BY sort')->fetchAll();
return array_column($rows, null, 'skey');
}
function to_num($v): ?float
{
return $v === null ? null : (float)$v;
}
const PARAMS_CACHE_TTL = 300; // seconds; the cache is also cleared whenever an admin saves
function params_cache_file(): string
{
$dir = (string)(app_config()['app']['cache_dir'] ?? '');
if ($dir === '') {
$dir = sys_get_temp_dir() . '/serubi-' . substr(md5(__DIR__), 0, 12);
}
return rtrim($dir, '/\\') . '/params.json';
}
/** Parameters in the shape assets/js/engine.js expects (cached to spare the database). */
function load_params(): array
{
$file = params_cache_file();
if (is_file($file) && time() - (int)@filemtime($file) < PARAMS_CACHE_TTL) {
$cached = json_decode((string)@file_get_contents($file), true);
if (is_array($cached) && isset($cached['settings'])) {
return $cached;
}
}
$params = load_params_db();
$dir = dirname($file);
if (is_dir($dir) || @mkdir($dir, 0700, true)) {
$tmp = $file . '.' . bin2hex(random_bytes(4));
if (@file_put_contents($tmp, json_encode($params, JSON_PRESERVE_ZERO_FRACTION)) !== false) {
@rename($tmp, $file);
}
@unlink($tmp);
}
return $params;
}
function params_cache_clear(): void
{
@unlink(params_cache_file());
}
function load_params_db(): array
{
$pdo = db();
$settings = [];
$updated = null;
foreach (settings_rows() as $key => $row) {
$settings[$key] = (float)$row['value'];
$updated = max($updated ?? '', $row['updated_at']);
}
$tenors = $pdo->query('SELECT months, cashback FROM tenors ORDER BY months')->fetchAll();
$productRates = array_fill_keys(array_keys(SERUBI_PRODUCTS), []);
foreach ($pdo->query('SELECT product, max_nominal, rate FROM product_rates ORDER BY product, sort, id') as $r) {
$productRates[$r['product']][] = ['max' => to_num($r['max_nominal']), 'rate' => (float)$r['rate']];
}
$bands = $pdo->query('SELECT id, max_nominal FROM deposito_bands ORDER BY sort, id')->fetchAll();
$rates = [];
foreach ($pdo->query('SELECT band_id, tenor, rate FROM deposito_rates') as $r) {
$rates[$r['band_id']][$r['tenor']] = (float)$r['rate'];
}
$depositoRates = [];
foreach ($tenors as $t) {
$m = (int)$t['months'];
$depositoRates[$m] = array_map(fn($b) => [
'max' => to_num($b['max_nominal']),
'rate' => $rates[$b['id']][$m] ?? 0.0,
], $bands);
}
$tiers = array_map(fn($r) => ['min' => (float)$r['min_nominal'], 'nasabah' => (float)$r['share_nasabah']],
$pdo->query('SELECT min_nominal, share_nasabah FROM cashback_tiers ORDER BY min_nominal')->fetchAll());
$last = $pdo->query("SELECT username, created_at FROM audit_log WHERE action LIKE 'param%' ORDER BY id DESC LIMIT 1")->fetch();
return [
'settings' => $settings,
'tenors' => array_map(fn($t) => (int)$t['months'], $tenors),
'cashbackTenors' => array_values(array_map(fn($t) => (int)$t['months'], array_filter($tenors, fn($t) => (int)$t['cashback'] === 1))),
'productRates' => $productRates,
'depositoBands' => array_map(fn($b) => to_num($b['max_nominal']), $bands),
'depositoRates' => $depositoRates,
'cashbackTiers' => $tiers,
'updatedAt' => $last['created_at'] ?? $updated,
'updatedBy' => $last['username'] ?? null,
];
}