123 lines
3.1 KiB
PHP
123 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support;
|
|
|
|
use App\Enums\Product\ProductBillingCycle;
|
|
use App\Models\Setting;
|
|
|
|
final class BillingCycleCatalog
|
|
{
|
|
public const SETTING_KEY = 'custom_billing_months';
|
|
|
|
public const CODE_PREFIX = 'months_';
|
|
|
|
public const MAX_MONTHS = 600;
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public static function builtInCodes(): array
|
|
{
|
|
return array_values(array_map(
|
|
fn (ProductBillingCycle $cycle) => $cycle->value,
|
|
array_filter(ProductBillingCycle::cases(), fn (ProductBillingCycle $cycle) => $cycle->months() > 0)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<int>
|
|
*/
|
|
public static function builtInMonths(): array
|
|
{
|
|
return array_values(array_filter(array_map(
|
|
fn (ProductBillingCycle $cycle) => $cycle->months(),
|
|
ProductBillingCycle::cases()
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* @return list<int>
|
|
*/
|
|
public static function customMonths(): array
|
|
{
|
|
$saved = json_decode((string) Setting::where('key', self::SETTING_KEY)->value('value'), true);
|
|
|
|
return self::normalizeMonths(is_array($saved) ? $saved : []);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, mixed> $months
|
|
* @return list<int>
|
|
*/
|
|
public static function saveCustomMonths(array $months): array
|
|
{
|
|
$normalized = self::normalizeMonths($months);
|
|
Setting::updateOrCreate(['key' => self::SETTING_KEY], ['value' => json_encode($normalized)]);
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
public static function codes(): array
|
|
{
|
|
$codes = [...self::builtInCodes(), ...array_map(self::codeFor(...), self::customMonths())];
|
|
usort($codes, fn (string $a, string $b) => self::monthsFor($a) <=> self::monthsFor($b));
|
|
|
|
return $codes;
|
|
}
|
|
|
|
public static function codeFor(int $months): string
|
|
{
|
|
return self::CODE_PREFIX . $months;
|
|
}
|
|
|
|
public static function monthsFor(?string $code): ?int
|
|
{
|
|
if ($code === null || $code === '') {
|
|
return null;
|
|
}
|
|
|
|
$builtIn = ProductBillingCycle::tryFrom($code);
|
|
if ($builtIn) {
|
|
return $builtIn->months();
|
|
}
|
|
|
|
return preg_match('/^' . self::CODE_PREFIX . '(\d{1,3})$/', $code, $m) && (int) $m[1] > 0
|
|
? (int) $m[1]
|
|
: null;
|
|
}
|
|
|
|
public static function labelForMonths(int $months): string
|
|
{
|
|
return $months > 0 && $months % 12 === 0 ? ($months / 12) . ' năm' : $months . ' tháng';
|
|
}
|
|
|
|
public static function labelForCode(?string $code): ?string
|
|
{
|
|
$months = self::monthsFor($code);
|
|
|
|
return $months ? self::labelForMonths($months) : null;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, mixed> $months
|
|
* @return list<int>
|
|
*/
|
|
private static function normalizeMonths(array $months): array
|
|
{
|
|
$valid = array_filter(
|
|
array_map('intval', $months),
|
|
fn (int $m) => $m >= 1 && $m <= self::MAX_MONTHS && !in_array($m, self::builtInMonths(), true)
|
|
);
|
|
|
|
$unique = array_values(array_unique($valid));
|
|
sort($unique);
|
|
|
|
return $unique;
|
|
}
|
|
}
|