Phase 113 complete (v3.7 Invoices): - DB: invoices, invoice_configs, invoice_number_counters, fakturownia_integration_settings + orders.invoice_requested - FakturowniaIntegrationRepository (multi-account via integrations.type='fakturownia') - FakturowniaApiClient (testConnection; createInvoice/downloadPdf STUBs) - IntegrationsRepository::updateTestResult() (reusable test-result writer) - /settings/integrations/fakturownia (list + edit + test + delete) - Karta Fakturownia w hubie /settings/integrations Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
175 lines
4.7 KiB
PHP
175 lines
4.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Settings;
|
|
|
|
use App\Core\Support\StringHelper;
|
|
use PDO;
|
|
use Throwable;
|
|
|
|
final class IntegrationsRepository
|
|
{
|
|
public function __construct(private readonly PDO $pdo)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
public function findByTypeAndName(string $type, string $name): ?array
|
|
{
|
|
try {
|
|
$statement = $this->pdo->prepare(
|
|
'SELECT * FROM integrations WHERE type = :type AND name = :name LIMIT 1'
|
|
);
|
|
$statement->execute([
|
|
'type' => trim($type),
|
|
'name' => trim($name),
|
|
]);
|
|
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
return is_array($row) ? $row : null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
public function findFirstByType(string $type): ?array
|
|
{
|
|
try {
|
|
$statement = $this->pdo->prepare(
|
|
'SELECT * FROM integrations WHERE type = :type ORDER BY id ASC LIMIT 1'
|
|
);
|
|
$statement->execute(['type' => trim($type)]);
|
|
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
return is_array($row) ? $row : null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
public function findById(int $id): ?array
|
|
{
|
|
if ($id <= 0) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$statement = $this->pdo->prepare('SELECT * FROM integrations WHERE id = :id LIMIT 1');
|
|
$statement->execute(['id' => $id]);
|
|
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
return is_array($row) ? $row : null;
|
|
}
|
|
|
|
public function ensureIntegration(
|
|
string $type,
|
|
string $name,
|
|
string $baseUrl,
|
|
int $timeoutSeconds = 10,
|
|
bool $isActive = true
|
|
): int {
|
|
$typeValue = trim($type);
|
|
$nameValue = trim($name);
|
|
$baseUrlValue = trim($baseUrl);
|
|
|
|
$existing = $this->findByTypeAndName($typeValue, $nameValue);
|
|
if ($existing !== null) {
|
|
return (int) ($existing['id'] ?? 0);
|
|
}
|
|
|
|
$statement = $this->pdo->prepare(
|
|
'INSERT INTO integrations (
|
|
type, name, base_url, timeout_seconds, is_active, created_at, updated_at
|
|
) VALUES (
|
|
:type, :name, :base_url, :timeout_seconds, :is_active, NOW(), NOW()
|
|
)'
|
|
);
|
|
$statement->execute([
|
|
'type' => $typeValue,
|
|
'name' => $nameValue,
|
|
'base_url' => $baseUrlValue,
|
|
'timeout_seconds' => max(1, $timeoutSeconds),
|
|
'is_active' => $isActive ? 1 : 0,
|
|
]);
|
|
|
|
return (int) $this->pdo->lastInsertId();
|
|
}
|
|
|
|
public function updateApiKeyEncrypted(int $integrationId, ?string $encrypted): void
|
|
{
|
|
if ($integrationId <= 0) {
|
|
return;
|
|
}
|
|
|
|
$statement = $this->pdo->prepare(
|
|
'UPDATE integrations
|
|
SET api_key_encrypted = :api_key_encrypted,
|
|
updated_at = NOW()
|
|
WHERE id = :id'
|
|
);
|
|
$statement->execute([
|
|
'id' => $integrationId,
|
|
'api_key_encrypted' => StringHelper::nullableString((string) $encrypted),
|
|
]);
|
|
}
|
|
|
|
public function updateTestResult(int $integrationId, string $status, ?int $httpCode, string $message): void
|
|
{
|
|
if ($integrationId <= 0) {
|
|
return;
|
|
}
|
|
|
|
$statement = $this->pdo->prepare(
|
|
'UPDATE integrations
|
|
SET last_test_status = :status,
|
|
last_test_http_code = :http_code,
|
|
last_test_message = :message,
|
|
last_test_at = NOW(),
|
|
updated_at = NOW()
|
|
WHERE id = :id'
|
|
);
|
|
$statement->execute([
|
|
'id' => $integrationId,
|
|
'status' => substr($status, 0, 16),
|
|
'http_code' => $httpCode,
|
|
'message' => substr($message, 0, 255),
|
|
]);
|
|
}
|
|
|
|
public function getApiKeyEncrypted(int $integrationId): ?string
|
|
{
|
|
if ($integrationId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$statement = $this->pdo->prepare(
|
|
'SELECT api_key_encrypted FROM integrations WHERE id = :id LIMIT 1'
|
|
);
|
|
$statement->execute(['id' => $integrationId]);
|
|
$value = $statement->fetchColumn();
|
|
} catch (Throwable) {
|
|
return null;
|
|
}
|
|
|
|
if (!is_string($value)) {
|
|
return null;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
return $trimmed === '' ? null : $trimmed;
|
|
}
|
|
|
|
}
|