refactor: odstraneni PSR-1 SideEffects warningu
- Handler funkce extrahovany z API souboru do api/admin/handlers/ - config.php rozdeleny na helpers.php (funkce) a constants.php (konstanty) - require_once odstranen z class souboru (AuditLog, JWTAuth, LeaveNotification) - vendor/autoload.php presunuto do config.php bootstrap - totp-handlers.php: pridany use deklarace pro TwoFactorAuth - phpstan.neon: bootstrapFiles, scanDirectories, dynamicConstantNames - Opraveny chybejici routing bloky v totp.php a session.php Vysledek: phpcs 0 errors 0 warnings, PHPStan 0 errors, ESLint 0 errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
247
api/admin/handlers/company-settings-handlers.php
Normal file
247
api/admin/handlers/company-settings-handlers.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @param bool $includeLogo false = bez logo_data BLOBu
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function getOrCreateSettings(PDO $pdo, bool $includeLogo = false): array
|
||||
{
|
||||
if ($includeLogo) {
|
||||
$stmt = $pdo->query('SELECT * FROM company_settings LIMIT 1');
|
||||
} else {
|
||||
$stmt = $pdo->query('
|
||||
SELECT id, company_name, company_id, vat_id, street, city, postal_code, country,
|
||||
quotation_prefix, default_currency, default_vat_rate,
|
||||
custom_fields, uuid, modified_at, sync_version,
|
||||
order_type_code, invoice_type_code, is_deleted,
|
||||
CASE WHEN logo_data IS NOT NULL AND LENGTH(logo_data) > 0 THEN 1 ELSE 0 END as has_logo
|
||||
FROM company_settings LIMIT 1
|
||||
');
|
||||
}
|
||||
$settings = $stmt->fetch();
|
||||
|
||||
if (!$settings) {
|
||||
$uuid = sprintf(
|
||||
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0x0fff) | 0x4000,
|
||||
random_int(0, 0x3fff) | 0x8000,
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff),
|
||||
random_int(0, 0xffff)
|
||||
);
|
||||
$pdo->prepare(
|
||||
"INSERT INTO company_settings
|
||||
(id, company_name, quotation_prefix, default_currency,
|
||||
default_vat_rate, uuid, modified_at, sync_version)
|
||||
VALUES (1, '', 'N', 'EUR', 21.0, ?, NOW(), 1)"
|
||||
)->execute([$uuid]);
|
||||
return getOrCreateSettings($pdo, $includeLogo);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
function handleGetOffersSettings(PDO $pdo): void
|
||||
{
|
||||
$settings = getOrCreateSettings($pdo, false);
|
||||
/** @var array<mixed>|null $cfRaw */
|
||||
$cfRaw = !empty($settings['custom_fields'])
|
||||
? json_decode($settings['custom_fields'], true)
|
||||
: null;
|
||||
if (is_array($cfRaw) && !isset($cfRaw['fields'])) {
|
||||
$settings['custom_fields'] = $cfRaw;
|
||||
$settings['supplier_field_order'] = null;
|
||||
} elseif (is_array($cfRaw) && isset($cfRaw['fields'])) {
|
||||
$settings['custom_fields'] = $cfRaw['fields'];
|
||||
$settings['supplier_field_order'] = $cfRaw['field_order'] ?? $cfRaw['fieldOrder'] ?? null;
|
||||
} else {
|
||||
$settings['custom_fields'] = [];
|
||||
$settings['supplier_field_order'] = null;
|
||||
}
|
||||
|
||||
$settings['has_logo'] = (bool)($settings['has_logo'] ?? false);
|
||||
|
||||
successResponse($settings);
|
||||
}
|
||||
|
||||
function handleUpdateOffersSettings(PDO $pdo): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
$settings = getOrCreateSettings($pdo);
|
||||
|
||||
// Delkove limity
|
||||
$maxLengths = [
|
||||
'company_name' => 255, 'street' => 255, 'city' => 255,
|
||||
'postal_code' => 20, 'country' => 100,
|
||||
'company_id' => 50, 'vat_id' => 50,
|
||||
'default_currency' => 5,
|
||||
];
|
||||
foreach ($maxLengths as $f => $max) {
|
||||
if (isset($input[$f]) && mb_strlen(trim((string)$input[$f])) > $max) {
|
||||
errorResponse("Pole $f je příliš dlouhé (max $max znaků)");
|
||||
}
|
||||
}
|
||||
// Validace meny
|
||||
if (isset($input['default_currency']) && !in_array($input['default_currency'], ['EUR', 'USD', 'CZK', 'GBP'])) {
|
||||
errorResponse('Neplatná měna');
|
||||
}
|
||||
|
||||
$fields = [
|
||||
'company_name', 'street', 'city', 'postal_code', 'country',
|
||||
'company_id', 'vat_id',
|
||||
'quotation_prefix', 'default_currency',
|
||||
'order_type_code', 'invoice_type_code',
|
||||
];
|
||||
|
||||
$setClauses = [];
|
||||
$params = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $input)) {
|
||||
$setClauses[] = "$field = ?";
|
||||
$params[] = $input[$field];
|
||||
}
|
||||
}
|
||||
|
||||
// custom_fields + SupplierFieldOrder - ulozeny dohromady jako JSON
|
||||
if (array_key_exists('custom_fields', $input) || array_key_exists('supplier_field_order', $input)) {
|
||||
/** @var array<mixed>|null $currentRaw */
|
||||
$currentRaw = !empty($settings['custom_fields'])
|
||||
? json_decode($settings['custom_fields'], true)
|
||||
: null;
|
||||
if (is_array($currentRaw) && !isset($currentRaw['fields'])) {
|
||||
/** @var array<string, mixed> $stored */
|
||||
$stored = ['fields' => $currentRaw, 'field_order' => null];
|
||||
} elseif (is_array($currentRaw) && isset($currentRaw['fields'])) {
|
||||
/** @var array<string, mixed> $stored */
|
||||
$stored = $currentRaw;
|
||||
} else {
|
||||
$stored = ['fields' => [], 'field_order' => null];
|
||||
}
|
||||
|
||||
if (array_key_exists('custom_fields', $input) && is_array($input['custom_fields'])) {
|
||||
$stored['fields'] = $input['custom_fields'];
|
||||
}
|
||||
if (array_key_exists('supplier_field_order', $input)) {
|
||||
$stored['field_order'] = is_array($input['supplier_field_order']) ? $input['supplier_field_order'] : null;
|
||||
}
|
||||
|
||||
// Odstranit stary klic
|
||||
unset($stored['fieldOrder']);
|
||||
|
||||
$setClauses[] = 'custom_fields = ?';
|
||||
$params[] = json_encode($stored, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// Validace prefixu
|
||||
if (isset($input['quotation_prefix']) && !preg_match('/^[A-Za-z0-9]{0,10}$/', $input['quotation_prefix'])) {
|
||||
errorResponse('Prefix nabídky může obsahovat pouze alfanumerické znaky (max 10)');
|
||||
}
|
||||
if (isset($input['order_type_code']) && !preg_match('/^[0-9]{0,10}$/', $input['order_type_code'])) {
|
||||
errorResponse('Typový kód objednávek může obsahovat pouze čísla (max 10)');
|
||||
}
|
||||
if (isset($input['invoice_type_code']) && !preg_match('/^[0-9]{0,10}$/', $input['invoice_type_code'])) {
|
||||
errorResponse('Typový kód faktur může obsahovat pouze čísla (max 10)');
|
||||
}
|
||||
|
||||
$numericFields = ['default_vat_rate'];
|
||||
foreach ($numericFields as $field) {
|
||||
if (array_key_exists($field, $input)) {
|
||||
$val = is_numeric($input[$field]) ? floatval($input[$field]) : 0;
|
||||
if ($val < 0 || $val > 100) {
|
||||
errorResponse('Sazba DPH musí být mezi 0 a 100');
|
||||
}
|
||||
$setClauses[] = "$field = ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($setClauses)) {
|
||||
errorResponse('Žádná data k aktualizaci');
|
||||
}
|
||||
|
||||
$setClauses[] = 'modified_at = NOW()';
|
||||
$setClauses[] = 'sync_version = sync_version + 1';
|
||||
|
||||
$sql = 'UPDATE company_settings SET ' . implode(', ', $setClauses) . ' WHERE id = ?';
|
||||
$params[] = $settings['id'];
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
|
||||
AuditLog::logUpdate('company_settings', (int)$settings['id'], [], $input, 'Aktualizováno nastavení firmy');
|
||||
|
||||
successResponse(null, 'Nastavení bylo uloženo');
|
||||
}
|
||||
|
||||
function handleUploadLogo(PDO $pdo): void
|
||||
{
|
||||
if (!isset($_FILES['logo']) || $_FILES['logo']['error'] !== UPLOAD_ERR_OK) {
|
||||
errorResponse('Nebyl nahrán žádný soubor');
|
||||
}
|
||||
|
||||
$file = $_FILES['logo'];
|
||||
$allowedTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_file($finfo, $file['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (!in_array($mimeType, $allowedTypes)) {
|
||||
errorResponse('Nepodporovaný formát obrázku. Povolené: PNG, JPEG, GIF, WebP');
|
||||
}
|
||||
|
||||
if ($file['size'] > 5 * 1024 * 1024) {
|
||||
errorResponse('Soubor je příliš velký (max 5 MB)');
|
||||
}
|
||||
|
||||
$logoData = file_get_contents($file['tmp_name']);
|
||||
|
||||
$settings = getOrCreateSettings($pdo);
|
||||
$stmt = $pdo->prepare(
|
||||
'UPDATE company_settings SET logo_data = ?, modified_at = NOW(), sync_version = sync_version + 1 WHERE id = ?'
|
||||
);
|
||||
$stmt->execute([$logoData, $settings['id']]);
|
||||
|
||||
|
||||
AuditLog::logUpdate(
|
||||
'company_settings',
|
||||
(int)$settings['id'],
|
||||
[],
|
||||
['logo' => 'uploaded'],
|
||||
'Aktualizováno logo firmy'
|
||||
);
|
||||
|
||||
successResponse(null, 'Logo bylo nahráno');
|
||||
}
|
||||
|
||||
function handleGetLogo(PDO $pdo): void
|
||||
{
|
||||
$stmt = $pdo->query('SELECT logo_data FROM company_settings LIMIT 1');
|
||||
$row = $stmt->fetch();
|
||||
|
||||
if (!$row || empty($row['logo_data'])) {
|
||||
http_response_code(404);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['success' => false, 'error' => 'Logo nenalezeno']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$logoData = $row['logo_data'];
|
||||
|
||||
// Detect image type from binary data
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_buffer($finfo, $logoData);
|
||||
finfo_close($finfo);
|
||||
|
||||
header('Content-Type: ' . $mimeType);
|
||||
header('Content-Length: ' . strlen($logoData));
|
||||
header('Cache-Control: public, max-age=3600');
|
||||
echo $logoData;
|
||||
exit();
|
||||
}
|
||||
Reference in New Issue
Block a user