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:
@@ -16,6 +16,7 @@ declare(strict_types=1);
|
||||
require_once dirname(__DIR__) . '/config.php';
|
||||
require_once dirname(__DIR__) . '/includes/JWTAuth.php';
|
||||
require_once dirname(__DIR__) . '/includes/AuditLog.php';
|
||||
require_once __DIR__ . '/handlers/customers-handlers.php';
|
||||
|
||||
setCorsHeaders();
|
||||
setSecurityHeaders();
|
||||
@@ -76,275 +77,3 @@ try {
|
||||
errorResponse('Chyba databáze', 500);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $customer */
|
||||
function parseCustomerCustomFields(array &$customer): void
|
||||
{
|
||||
/** @var array<mixed>|null $cfRaw */
|
||||
$cfRaw = !empty($customer['custom_fields'])
|
||||
? json_decode($customer['custom_fields'], true)
|
||||
: null;
|
||||
if (is_array($cfRaw) && !isset($cfRaw['fields'])) {
|
||||
$customer['custom_fields'] = $cfRaw;
|
||||
$customer['customer_field_order'] = null;
|
||||
} elseif (is_array($cfRaw) && isset($cfRaw['fields'])) {
|
||||
$customer['custom_fields'] = $cfRaw['fields'];
|
||||
$customer['customer_field_order'] = $cfRaw['field_order'] ?? $cfRaw['fieldOrder'] ?? null;
|
||||
} else {
|
||||
$customer['custom_fields'] = [];
|
||||
$customer['customer_field_order'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $input */
|
||||
function encodeCustomerCustomFields(array $input, ?string $existingJson): ?string
|
||||
{
|
||||
if (!array_key_exists('custom_fields', $input) && !array_key_exists('customer_field_order', $input)) {
|
||||
return $existingJson;
|
||||
}
|
||||
/** @var array<mixed>|null $currentRaw */
|
||||
$currentRaw = !empty($existingJson) ? json_decode($existingJson, 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('customer_field_order', $input)) {
|
||||
$stored['field_order'] = is_array($input['customer_field_order']) ? $input['customer_field_order'] : null;
|
||||
}
|
||||
|
||||
unset($stored['fieldOrder']);
|
||||
|
||||
return json_encode($stored, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
function handleGetAll(PDO $pdo): void
|
||||
{
|
||||
$stmt = $pdo->query('
|
||||
SELECT c.*, COUNT(q.id) as quotation_count
|
||||
FROM customers c
|
||||
LEFT JOIN quotations q ON q.customer_id = c.id
|
||||
GROUP BY c.id
|
||||
ORDER BY c.name ASC
|
||||
');
|
||||
$customers = $stmt->fetchAll();
|
||||
|
||||
foreach ($customers as &$c) {
|
||||
parseCustomerCustomFields($c);
|
||||
}
|
||||
unset($c);
|
||||
|
||||
successResponse(['customers' => $customers]);
|
||||
}
|
||||
|
||||
function handleGetOne(PDO $pdo, int $id): void
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT * FROM customers WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$customer = $stmt->fetch();
|
||||
|
||||
if (!$customer) {
|
||||
errorResponse('Zákazník nebyl nalezen', 404);
|
||||
}
|
||||
|
||||
parseCustomerCustomFields($customer);
|
||||
successResponse($customer);
|
||||
}
|
||||
|
||||
function handleSearch(PDO $pdo): void
|
||||
{
|
||||
$q = trim($_GET['q'] ?? '');
|
||||
if (strlen($q) < 1 || mb_strlen($q) > 100) {
|
||||
successResponse(['customers' => []]);
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
SELECT * FROM customers
|
||||
WHERE name LIKE ? OR company_id LIKE ? OR city LIKE ?
|
||||
ORDER BY name ASC
|
||||
LIMIT 20
|
||||
');
|
||||
$search = "%{$q}%";
|
||||
$stmt->execute([$search, $search, $search]);
|
||||
|
||||
$results = $stmt->fetchAll();
|
||||
foreach ($results as &$c) {
|
||||
parseCustomerCustomFields($c);
|
||||
}
|
||||
unset($c);
|
||||
successResponse(['customers' => $results]);
|
||||
}
|
||||
|
||||
function handleCreateCustomer(PDO $pdo): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
|
||||
if (empty($input['name'])) {
|
||||
errorResponse('Název zákazníka je povinný');
|
||||
}
|
||||
if (mb_strlen($input['name']) > 255) {
|
||||
errorResponse('Název zákazníka je příliš dlouhý (max 255 znaků)');
|
||||
}
|
||||
foreach (['street', 'city', 'country'] as $f) {
|
||||
if (isset($input[$f]) && mb_strlen($input[$f]) > 255) {
|
||||
errorResponse("Pole $f je příliš dlouhé (max 255 znaků)");
|
||||
}
|
||||
}
|
||||
if (isset($input['postal_code']) && mb_strlen($input['postal_code']) > 20) {
|
||||
errorResponse('PSČ je příliš dlouhé (max 20 znaků)');
|
||||
}
|
||||
if (isset($input['company_id']) && mb_strlen($input['company_id']) > 50) {
|
||||
errorResponse('IČO je příliš dlouhé (max 50 znaků)');
|
||||
}
|
||||
if (isset($input['vat_id']) && mb_strlen($input['vat_id']) > 50) {
|
||||
errorResponse('DIČ je příliš dlouhé (max 50 znaků)');
|
||||
}
|
||||
|
||||
$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)
|
||||
);
|
||||
|
||||
$customFieldsJson = encodeCustomerCustomFields($input, null);
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO customers (name, street, city, postal_code, country,
|
||||
company_id, vat_id, custom_fields, created_at, uuid, modified_at, sync_version)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, NOW(), 1)
|
||||
');
|
||||
$stmt->execute([
|
||||
$input['name'],
|
||||
$input['street'] ?? '',
|
||||
$input['city'] ?? '',
|
||||
$input['postal_code'] ?? '',
|
||||
$input['country'] ?? '',
|
||||
$input['company_id'] ?? '',
|
||||
$input['vat_id'] ?? '',
|
||||
$customFieldsJson,
|
||||
$uuid,
|
||||
]);
|
||||
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
|
||||
AuditLog::logCreate('customer', (int)$newId, [
|
||||
'name' => $input['name'],
|
||||
], "Vytvořen zákazník '{$input['name']}'");
|
||||
|
||||
successResponse(['id' => $newId], 'Zákazník byl vytvořen');
|
||||
}
|
||||
|
||||
function handleUpdateCustomer(PDO $pdo, int $id): void
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT * FROM customers WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$existing = $stmt->fetch();
|
||||
|
||||
if (!$existing) {
|
||||
errorResponse('Zákazník nebyl nalezen', 404);
|
||||
}
|
||||
|
||||
$input = getJsonInput();
|
||||
|
||||
// Delkove limity
|
||||
if (isset($input['name']) && mb_strlen($input['name']) > 255) {
|
||||
errorResponse('Název je příliš dlouhý (max 255 znaků)');
|
||||
}
|
||||
foreach (['street', 'city', 'country'] as $f) {
|
||||
if (isset($input[$f]) && mb_strlen($input[$f]) > 255) {
|
||||
errorResponse("Pole $f je příliš dlouhé (max 255 znaků)");
|
||||
}
|
||||
}
|
||||
if (isset($input['postal_code']) && mb_strlen($input['postal_code']) > 20) {
|
||||
errorResponse('PSČ je příliš dlouhé (max 20 znaků)');
|
||||
}
|
||||
if (isset($input['company_id']) && mb_strlen($input['company_id']) > 50) {
|
||||
errorResponse('IČO je příliš dlouhé (max 50 znaků)');
|
||||
}
|
||||
if (isset($input['vat_id']) && mb_strlen($input['vat_id']) > 50) {
|
||||
errorResponse('DIČ je příliš dlouhé (max 50 znaků)');
|
||||
}
|
||||
|
||||
$customFieldsJson = encodeCustomerCustomFields($input, $existing['custom_fields'] ?? null);
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
UPDATE customers SET
|
||||
name = ?,
|
||||
street = ?,
|
||||
city = ?,
|
||||
postal_code = ?,
|
||||
country = ?,
|
||||
company_id = ?,
|
||||
vat_id = ?,
|
||||
custom_fields = ?,
|
||||
modified_at = NOW(),
|
||||
sync_version = sync_version + 1
|
||||
WHERE id = ?
|
||||
');
|
||||
$stmt->execute([
|
||||
$input['name'] ?? $existing['name'],
|
||||
$input['street'] ?? $existing['street'],
|
||||
$input['city'] ?? $existing['city'],
|
||||
$input['postal_code'] ?? $existing['postal_code'],
|
||||
$input['country'] ?? $existing['country'],
|
||||
$input['company_id'] ?? $existing['company_id'],
|
||||
$input['vat_id'] ?? $existing['vat_id'],
|
||||
$customFieldsJson,
|
||||
$id,
|
||||
]);
|
||||
|
||||
|
||||
AuditLog::logUpdate(
|
||||
'customer',
|
||||
$id,
|
||||
['name' => $existing['name']],
|
||||
['name' => $input['name'] ?? $existing['name']],
|
||||
"Upraven zákazník #$id"
|
||||
);
|
||||
|
||||
successResponse(null, 'Zákazník byl aktualizován');
|
||||
}
|
||||
|
||||
function handleDeleteCustomer(PDO $pdo, int $id): void
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT * FROM customers WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$customer = $stmt->fetch();
|
||||
|
||||
if (!$customer) {
|
||||
errorResponse('Zákazník nebyl nalezen', 404);
|
||||
}
|
||||
|
||||
// Check if customer has quotations
|
||||
$stmt = $pdo->prepare('SELECT COUNT(*) FROM quotations WHERE customer_id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$count = (int)$stmt->fetchColumn();
|
||||
|
||||
if ($count > 0) {
|
||||
errorResponse("Zákazníka nelze smazat, má $count nabídek");
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('DELETE FROM customers WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
|
||||
|
||||
AuditLog::logDelete('customer', $id, ['name' => $customer['name']], "Smazán zákazník '{$customer['name']}'");
|
||||
|
||||
successResponse(null, 'Zákazník byl smazán');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user