MasterDB + layout
parent
5f2bb30ee4
commit
2398bd6aca
|
|
@ -11,11 +11,13 @@ use Nette\Security\User;
|
|||
class KufrikConnectionFactory
|
||||
{
|
||||
private User $user;
|
||||
private array $baseConfig;
|
||||
private string $dbUser;
|
||||
private string $dbPassword;
|
||||
|
||||
public function __construct(array $baseConfig, User $user)
|
||||
public function __construct(string $dbUser, string $dbPassword, User $user)
|
||||
{
|
||||
$this->baseConfig = $baseConfig;
|
||||
$this->dbUser = $dbUser;
|
||||
$this->dbPassword = $dbPassword;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
|
|
@ -27,23 +29,21 @@ class KufrikConnectionFactory
|
|||
|
||||
$identity = $this->user->getIdentity();
|
||||
|
||||
// 1. Dynamické načtení z identity (každý uživatel může mít jiný server i DB!)
|
||||
$host = $identity->db_host ?? $this->baseConfig['host'];
|
||||
$databaseName = $identity->db_name ?? $this->baseConfig['default_db'];
|
||||
$firmaId = $identity->firma_id ?? null;
|
||||
// Server i Databáze se načtou z Identity přihlášeného uživatele (z tabulky LOGIN)
|
||||
$host = $identity->server ?? null;
|
||||
$databaseName = $identity->database ?? null;
|
||||
|
||||
if (!$firmaId) {
|
||||
throw new \LogicException('Uživatel nemá zvolenou žádnou aktivní firmu.');
|
||||
if (empty($host) || empty($databaseName)) {
|
||||
throw new \LogicException('Přihlášený uživatel nemá v tabulce LOGIN definovaný SERVER nebo DATABASE.');
|
||||
}
|
||||
|
||||
// 2. Sestavení DSN pro konkrétní SQL Server a DB
|
||||
$dsn = "sqlsrv:Server={$host};Database={$databaseName}";
|
||||
// Dynamický DSN řetězec
|
||||
$dsn = "sqlsrv:Server={$host};Database={$databaseName};LoginTimeout=3;";
|
||||
|
||||
// Přihlašovací údaje k SQL serveru (buď společné ze souboru, nebo dynamické)
|
||||
$connection = new Connection(
|
||||
return new Connection(
|
||||
$dsn,
|
||||
$this->baseConfig['username'],
|
||||
$this->baseConfig['password'],
|
||||
$this->dbUser,
|
||||
$this->dbPassword,
|
||||
[
|
||||
'lazy' => false,
|
||||
'driverOptions' => [
|
||||
|
|
@ -51,11 +51,6 @@ class KufrikConnectionFactory
|
|||
]
|
||||
]
|
||||
);
|
||||
|
||||
// 3. Nastavení RLS kontextu pro firmu
|
||||
$connection->query('EXEC sp_set_session_context @key = N\'FirmaId\', @value = ?', $firmaId);
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
public function createExplorer(): Explorer
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ class Authenticator implements IAuthenticator
|
|||
|
||||
// 1. Načtení uživatele podle tvého přesného schématu UZIVATEL
|
||||
$row = $this->masterDb->fetch(
|
||||
'SELECT ID, EMAIL, HESLO_HASH, JMENO, PRIJMENI, DATABASE_NAME, CONNECTION_STRING_KEY
|
||||
FROM dbo.UZIVATEL
|
||||
'SELECT ID, EMAIL, HESLO_HASH, [DATABASE], [SERVER], FIRMA_ID
|
||||
FROM dbo.LOGIN
|
||||
WHERE EMAIL = ? AND AKTIVNI = 1',
|
||||
$email
|
||||
);
|
||||
|
|
@ -43,18 +43,18 @@ class Authenticator implements IAuthenticator
|
|||
|
||||
// 2. Aktualizace času posledního přihlášení
|
||||
$this->masterDb->query(
|
||||
'UPDATE dbo.UZIVATEL SET POSLEDNI_PRIHLASENI = SYSDATETIME() WHERE ID = ?',
|
||||
'UPDATE dbo.LOGIN SET POSLEDNI_PRIHLASENI = SYSDATETIME() WHERE ID = ?',
|
||||
$row->ID
|
||||
);
|
||||
|
||||
// 3. Načtení výchozí FIRMA_ID z vazební tabulky UZIVATEL_FIRMA (pokud existuje)
|
||||
$firmaRow = $this->masterDb->fetch(
|
||||
'SELECT TOP 1 FIRMA_ID FROM dbo.UZIVATEL_FIRMA WHERE UZIVATEL_ID = ?',
|
||||
$row->ID
|
||||
);
|
||||
// $firmaRow = $this->masterDb->fetch(
|
||||
// 'SELECT TOP 1 FIRMA_ID FROM dbo.UZIVATEL_FIRMA WHERE UZIVATEL_ID = ?',
|
||||
// $row->ID
|
||||
// );
|
||||
|
||||
// Fallback: Pokud vazební tabulka ještě není/nepoužívá se, použije se ID uživatele
|
||||
$firmaId = $firmaRow ? $firmaRow->FIRMA_ID : $row->ID;
|
||||
//$firmaId = $firmaRow ? $firmaRow->FIRMA_ID : $row->ID;
|
||||
|
||||
// 4. Předání dat do Nette Identity
|
||||
return new SimpleIdentity(
|
||||
|
|
@ -62,10 +62,9 @@ class Authenticator implements IAuthenticator
|
|||
null, // role
|
||||
[
|
||||
'email' => $row->EMAIL,
|
||||
'jmeno' => trim(($row->JMENO ?? '') . ' ' . ($row->PRIJMENI ?? '')),
|
||||
'db_name' => $row->DATABASE_NAME, // Nulllable -> KufrikConnectionFactory použije default_db
|
||||
'connection_key' => $row->CONNECTION_STRING_KEY,
|
||||
'firma_id' => $firmaId,
|
||||
'database' => $row->DATABASE, // Nulllable -> KufrikConnectionFactory použije default_db
|
||||
'server' => $row->SERVER,
|
||||
'firma_id' => (int) $row->FIRMA_ID, // Zpřístupní $user->identity->firma_id
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 1. HORNÍ LIŠTA -->
|
||||
<!-- 1. HORNÍ LIŠTA -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark app-header">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#"><i class="bi bi-briefcase-fill me-2"></i>Kufřík</a>
|
||||
|
|
@ -34,23 +34,34 @@
|
|||
<!-- Hlavní kategorie - viditelné pouze pro přihlášené -->
|
||||
{if $user->isLoggedIn()}
|
||||
<ul class="navbar-nav me-auto nav nav-tabs" id="mainCategoryTabs" role="tablist">
|
||||
<!-- FAKTURY -->
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="cat-invoices-tab" data-bs-toggle="tab" data-bs-target="#toolbar-invoices" type="button" role="tab">
|
||||
<button n:class="nav-link, $presenter->isLinkCurrent('Faktura:*') ? active"
|
||||
id="cat-invoices-tab" data-bs-toggle="tab" data-bs-target="#toolbar-invoices" type="button" role="tab">
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Faktury
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<!-- ZÁKAZNÍCI -->
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="cat-customers-tab" data-bs-toggle="tab" data-bs-target="#toolbar-customers" type="button" role="tab">
|
||||
<button n:class="nav-link, $presenter->isLinkCurrent('Zakaznik:*') ? active"
|
||||
id="cat-customers-tab" data-bs-toggle="tab" data-bs-target="#toolbar-customers" type="button" role="tab">
|
||||
<i class="bi bi-people me-1"></i> Zákazníci
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<!-- MOJE OSVČ -->
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="cat-profile-tab" data-bs-toggle="tab" data-bs-target="#toolbar-profile" type="button" role="tab">
|
||||
<button n:class="nav-link, $presenter->isLinkCurrent('Firma:*') ? active"
|
||||
id="cat-profile-tab" data-bs-toggle="tab" data-bs-target="#toolbar-profile" type="button" role="tab">
|
||||
<i class="bi bi-building me-1"></i> Moje OSVČ
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<!-- KUFŘÍK -->
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="cat-briefcase-tab" data-bs-toggle="tab" data-bs-target="#toolbar-briefcase" type="button" role="tab">
|
||||
<button n:class="nav-link, $presenter->isLinkCurrent('Uzivatel:*') ? active"
|
||||
id="cat-briefcase-tab" data-bs-toggle="tab" data-bs-target="#toolbar-briefcase" type="button" role="tab">
|
||||
<i class="bi bi-box me-1"></i> Kufřík
|
||||
</button>
|
||||
</li>
|
||||
|
|
@ -95,7 +106,7 @@
|
|||
<div class="tab-content" id="actionToolbarContent">
|
||||
|
||||
<!-- FAKTURY -->
|
||||
<div class="tab-pane fade show active" id="toolbar-invoices" role="tabpanel">
|
||||
<div n:class="tab-pane, fade, $presenter->isLinkCurrent('Faktura:*') ? 'show active'" id="toolbar-invoices" role="tabpanel">
|
||||
<div class="toolbar-group">
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="btn btn-outline-primary btn-action">
|
||||
|
|
@ -140,7 +151,7 @@
|
|||
</div>
|
||||
|
||||
<!-- ZÁKAZNÍCI -->
|
||||
<div class="tab-pane fade" id="toolbar-customers" role="tabpanel">
|
||||
<div n:class="tab-pane, fade, $presenter->isLinkCurrent('Zakaznik:*') ? 'show active'" id="toolbar-customers" role="tabpanel">
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="btn btn-outline-primary btn-action">
|
||||
<i class="bi bi-people-fill"></i>
|
||||
|
|
@ -154,12 +165,12 @@
|
|||
</div>
|
||||
|
||||
<!-- MOJE OSVČ -->
|
||||
<div class="tab-pane fade" id="toolbar-profile" role="tabpanel">
|
||||
<div n:class="tab-pane, fade, $presenter->isLinkCurrent('Firma:*') ? 'show active'" id="toolbar-profile" role="tabpanel">
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="btn btn-outline-primary btn-action">
|
||||
<a n:href="Firma:edit" n:class="btn, btn-action, $presenter->isLinkCurrent('Firma:edit') ? 'btn-primary active' : 'btn-outline-primary'">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
<span>Upravit moje OSVČ</span>
|
||||
</button>
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-secondary btn-action">
|
||||
<i class="bi bi-gear-fill"></i>
|
||||
<span>Nastavení</span>
|
||||
|
|
@ -168,8 +179,12 @@
|
|||
</div>
|
||||
|
||||
<!-- KUFŘÍK -->
|
||||
<div class="tab-pane fade" id="toolbar-briefcase" role="tabpanel">
|
||||
<div n:class="tab-pane, fade, $presenter->isLinkCurrent('Uzivatel:*') ? 'show active'" id="toolbar-briefcase" role="tabpanel">
|
||||
<div class="action-buttons">
|
||||
<a n:href="Uzivatel:edit" n:class="btn, btn-action, $presenter->isLinkCurrent('Uzivatel:edit') ? 'btn-primary active' : 'btn-outline-primary'">
|
||||
<i class="bi bi-person-gear"></i>
|
||||
<span>Profil a připojení</span>
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-info btn-action">
|
||||
<i class="bi bi-question-circle-fill"></i>
|
||||
<span>Nápověda</span>
|
||||
|
|
@ -188,6 +203,31 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Toasty -->
|
||||
<div n:foreach="$flashes as $flash" class="alert alert-{$flash->type} alert-dismissible fade show auto-dismiss mb-1 shadow-sm" role="alert">
|
||||
<i class="bi bi-check-circle-fill me-2"></i>
|
||||
<strong>{$flash->message}</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Najde všechny alerty s třídou auto-dismiss
|
||||
const alerts = document.querySelectorAll('.alert.auto-dismiss');
|
||||
|
||||
alerts.forEach(alert => {
|
||||
setTimeout(() => {
|
||||
// Přidá plynulé vytracení přes Bootstrap třídu nebo JS transition
|
||||
alert.classList.remove('show');
|
||||
alert.classList.add('fade');
|
||||
|
||||
// Po dokončení animace prvek úplně odstraní z DOMu
|
||||
setTimeout(() => alert.remove(), 500);
|
||||
}, 3000); // 3000 ms = 3 vteřiny
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 3. HLAVNÍ OBSAH -->
|
||||
<main class="main-content">
|
||||
<div class="content-card">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\UI\Faktura;
|
||||
|
||||
use App\UI\BasePresenter;
|
||||
use Nette\Database\Explorer;
|
||||
|
||||
class FakturaPresenter extends BasePresenter
|
||||
{
|
||||
public function __construct(
|
||||
private Explorer $kufrikExplorer
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function actionDefault(): void
|
||||
{
|
||||
// Seznam faktur
|
||||
}
|
||||
|
||||
public function actionAdd(): void
|
||||
{
|
||||
// Přidání faktury
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace App\UI\Firma;
|
||||
|
||||
use App\UI\BasePresenter;
|
||||
use Nette\Application\UI\Form;
|
||||
use Nette\Database\Explorer;
|
||||
|
||||
class FirmaPresenter extends BasePresenter
|
||||
{
|
||||
/**
|
||||
* Vstříkne se dynamický klientský Explorer
|
||||
*/
|
||||
public function __construct(
|
||||
private Explorer $kufrikExplorer
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function actionEdit(): void
|
||||
{
|
||||
$firmaId = $this->getUser()->getIdentity()->firma_id ?? null;
|
||||
|
||||
if (!$firmaId) {
|
||||
$this->flashMessage('K účtu není přiřazena žádná aktivní firma.', 'danger');
|
||||
$this->redirect('Home:');
|
||||
}
|
||||
|
||||
$firma = $this->kufrikExplorer->table('FIRMA')->get($firmaId);
|
||||
|
||||
if (!$firma) {
|
||||
$this->flashMessage('Firma nebyla v databázi nalezena.', 'danger');
|
||||
$this->redirect('Home:');
|
||||
}
|
||||
|
||||
// Predvyplnění formuláře
|
||||
$this['firmaForm']->setDefaults([
|
||||
'nazev' => $firma->NAZEV,
|
||||
'ic' => $firma->IC,
|
||||
'dic' => $firma->DIC,
|
||||
'platce_dph' => (bool) $firma->PLATCE_DPH,
|
||||
'telefon' => $firma->TELEFON,
|
||||
'email' => $firma->EMAIL,
|
||||
'ulice' => $firma->ULICE,
|
||||
'mesto' => $firma->MESTO,
|
||||
'psc' => $firma->PSC,
|
||||
'cislo_uctu' => $firma->CISLO_UCTU,
|
||||
'banka_kod' => $firma->BANKA_KOD,
|
||||
'iban' => $firma->IBAN,
|
||||
'bic' => $firma->BIC,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function createComponentFirmaForm(): Form
|
||||
{
|
||||
$form = new Form();
|
||||
|
||||
// Základní údaje
|
||||
$form->addText('nazev', 'Název firmy / OSVČ:')
|
||||
->setRequired('Zadejte název firmy nebo Vaše jméno.');
|
||||
|
||||
$form->addText('ic', 'IČO:')
|
||||
->setMaxLength(8);
|
||||
|
||||
$form->addText('dic', 'DIČ:')
|
||||
->setMaxLength(12);
|
||||
|
||||
$form->addCheckbox('platce_dph', 'Jsem plátce DPH');
|
||||
|
||||
// Kontakty
|
||||
$form->addText('telefon', 'Telefon:');
|
||||
$form->addEmail('email', 'E-mail:');
|
||||
|
||||
// Sídlo
|
||||
$form->addText('ulice', 'Ulice a ČP:');
|
||||
$form->addText('mesto', 'Město:');
|
||||
$form->addText('psc', 'PSČ:');
|
||||
|
||||
// Bankovní spojení
|
||||
$form->addText('cislo_uctu', 'Číslo účtu:');
|
||||
$form->addText('banka_kod', 'Kód banky:');
|
||||
$form->addText('iban', 'IBAN:');
|
||||
$form->addText('bic', 'BIC / SWIFT:');
|
||||
|
||||
$form->addSubmit('send', 'Uložit firmu');
|
||||
|
||||
$form->onSuccess[] = [$this, 'firmaFormSucceeded'];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function firmaFormSucceeded(Form $form, \stdClass $data): void
|
||||
{
|
||||
$firmaId = $this->getUser()->getIdentity()->firma_id;
|
||||
|
||||
try {
|
||||
$this->kufrikExplorer->table('FIRMA')
|
||||
->where('ID', $firmaId)
|
||||
->update([
|
||||
'NAZEV' => $data->nazev,
|
||||
'IC' => $data->ic,
|
||||
'DIC' => $data->dic,
|
||||
'PLATCE_DPH' => $data->platce_dph ? 1 : 0,
|
||||
'TELEFON' => $data->telefon,
|
||||
'EMAIL' => $data->email,
|
||||
'ULICE' => $data->ulice,
|
||||
'MESTO' => $data->mesto,
|
||||
'PSC' => $data->psc,
|
||||
'CISLO_UCTU' => $data->cislo_uctu,
|
||||
'BANKA_KOD' => $data->banka_kod,
|
||||
'IBAN' => $data->iban,
|
||||
'BIC' => $data->bic,
|
||||
]);
|
||||
|
||||
// Potvrzení s časovým razítkem uložení
|
||||
$casUlozeni = (new \DateTime())->format('H:i:s');
|
||||
$this->flashMessage("Údaje firmy byly úspěšně uloženy v {$casUlozeni}.", 'success');
|
||||
|
||||
} catch (\Nette\Application\AbortException $e) {
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$form->addError('Chyba při ukládání firmy: ' . $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->redirect('this');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
{block content}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10 col-lg-8">
|
||||
|
||||
<div class="content-card my-3">
|
||||
<h2 class="h4 mb-3" style="color: var(--header-bg);">
|
||||
<i class="bi bi-building me-2"></i>Moje OSVČ / Firma
|
||||
</h2>
|
||||
|
||||
<hr style="border-color: var(--border-color); margin-bottom: 1.5rem;">
|
||||
|
||||
<form n:name="firmaForm" class="login-form">
|
||||
|
||||
<div n:if="$form->ownErrors" class="alert alert-danger mb-3">
|
||||
<ul class="mb-0 ps-3">
|
||||
<li n:foreach="$form->ownErrors as $error">{$error}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- ZÁKLADNÍ ÚDAJE -->
|
||||
<h6 class="fw-bold text-muted mb-3"><i class="bi bi-card-heading me-1"></i> Základní údaje</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-6 form-group">
|
||||
<label n:name="nazev">Název / Jméno OSVČ</label>
|
||||
<input n:name="nazev" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-3 form-group">
|
||||
<label n:name="ic">IČO</label>
|
||||
<input n:name="ic" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-3 form-group">
|
||||
<label n:name="dic">DIČ</label>
|
||||
<input n:name="dic" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check mt-2 mb-3">
|
||||
<input n:name="platce_dph" class="form-check-input">
|
||||
<label n:name="platce_dph" class="form-check-label fw-bold">Plátce DPH</label>
|
||||
</div>
|
||||
|
||||
<!-- KONTAKTY & SÍDLO -->
|
||||
<h6 class="fw-bold text-muted mt-4 mb-3"><i class="bi bi-geo-alt me-1"></i> Sídlo a kontakty</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-4 form-group">
|
||||
<label n:name="ulice">Ulice a ČP</label>
|
||||
<input n:name="ulice" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-4 form-group">
|
||||
<label n:name="mesto">Město</label>
|
||||
<input n:name="mesto" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-4 form-group">
|
||||
<label n:name="psc">PSČ</label>
|
||||
<input n:name="psc" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-6 form-group">
|
||||
<label n:name="telefon">Telefon</label>
|
||||
<input n:name="telefon" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6 form-group">
|
||||
<label n:name="email">E-mail</label>
|
||||
<input n:name="email" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BANKA -->
|
||||
<h6 class="fw-bold text-muted mt-4 mb-3"><i class="bi bi-bank me-1"></i> Bankovní spojení</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-8 form-group">
|
||||
<label n:name="cislo_uctu">Číslo účtu</label>
|
||||
<input n:name="cislo_uctu" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-4 form-group">
|
||||
<label n:name="banka_kod">Kód banky</label>
|
||||
<input n:name="banka_kod" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-8 form-group">
|
||||
<label n:name="iban">IBAN</label>
|
||||
<input n:name="iban" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-4 form-group">
|
||||
<label n:name="bic">SWIFT / BIC</label>
|
||||
<input n:name="bic" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2 mt-4">
|
||||
<button n:name="send" class="btn btn-success">
|
||||
<i class="bi bi-check-lg me-1"></i> Uložit firmu
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\UI\Uzivatel;
|
||||
|
||||
use App\UI\BasePresenter;
|
||||
use Nette\Database\Explorer;
|
||||
|
||||
class UzivatelPresenter extends BasePresenter
|
||||
{
|
||||
public function __construct(
|
||||
private Explorer $kufrikExplorer
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function actionDefault(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function actionAdd(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\UI\Zakaznik;
|
||||
|
||||
use App\UI\BasePresenter;
|
||||
use Nette\Database\Explorer;
|
||||
|
||||
class ZakaznikPresenter extends BasePresenter
|
||||
{
|
||||
public function __construct(
|
||||
private Explorer $kufrikExplorer
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function actionDefault(): void
|
||||
{
|
||||
// Seznam zákazníků
|
||||
}
|
||||
|
||||
public function actionAdd(): void
|
||||
{
|
||||
// Přidání zákazníka
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
parameters:
|
||||
# Přihlašovací údaje ke klientským DB serverům
|
||||
mssql:
|
||||
host: "localhost" # Výchozí záložní host
|
||||
username: "kufrik"
|
||||
password: "giteaKufrik_2026!"
|
||||
default_db: "kufrik"
|
||||
|
||||
# Přihlašovací údaje k MS SQL Serveru pro klientské DB
|
||||
clientDb:
|
||||
user: kufrik
|
||||
password: 'giteaKufrik_2026!'
|
||||
|
||||
application:
|
||||
errorPresenter:
|
||||
|
|
@ -15,7 +12,8 @@ application:
|
|||
|
||||
|
||||
database:
|
||||
dsn: 'sqlsrv:server=localhost;Database=kufrik;LoginTimeout=3;'
|
||||
# Přihlašovací údaje k master DB
|
||||
dsn: 'sqlsrv:server=localhost;Database=kufrik_master;LoginTimeout=3;'
|
||||
user: 'kufrik'
|
||||
password: 'giteaKufrik_2026!'
|
||||
autowired: false # Vypnuto, abychom jako výchozí Explorer používali kufrikExplorer
|
||||
|
|
|
|||
|
|
@ -6,16 +6,14 @@ services:
|
|||
# Authenticator si vyžádá připojení k Master DB
|
||||
- App\Model\Security\Authenticator(@database.default.connection)
|
||||
|
||||
# Konfigurace pro klientskou KufrikConnectionFactory
|
||||
# 1. Factory pro klientskou databázi
|
||||
kufrikConnectionFactory:
|
||||
factory: App\Model\Database\KufrikConnectionFactory
|
||||
arguments:
|
||||
baseConfig: %mssql%
|
||||
factory: App\Model\Database\KufrikConnectionFactory(%clientDb.user%, %clientDb.password%)
|
||||
|
||||
# Hlavní databázový Explorer pro aplikaci
|
||||
# 2. Samotný Explorer pro klientská data (vyvolá metodu createExplorer)
|
||||
kufrikExplorer:
|
||||
type: Nette\Database\Explorer
|
||||
factory: @App\Model\Database\KufrikConnectionFactory::createExplorer()
|
||||
factory: @kufrikConnectionFactory::createExplorer()
|
||||
|
||||
search:
|
||||
- in: %appDir%
|
||||
|
|
|
|||
185
sql/KufrikDB.sql
185
sql/KufrikDB.sql
|
|
@ -1,8 +1,80 @@
|
|||
use master
|
||||
GO
|
||||
|
||||
DROP TABLE [kufrik]
|
||||
-- ==========================================
|
||||
-- MASTER databáze pro přihlašování a rozřazování
|
||||
-- ==========================================
|
||||
|
||||
CREATE DATABASE [kufrik_master]
|
||||
-- CONTAINMENT = NONE
|
||||
-- ON PRIMARY
|
||||
--( NAME = N'kufrik', FILENAME = N'/var/opt/mssql/data/kufrik.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
|
||||
-- LOG ON
|
||||
--( NAME = N'kufrik_log', FILENAME = N'/var/opt/mssql/data/kufrik_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
|
||||
GO
|
||||
ALTER DATABASE [kufrik_master] SET COMPATIBILITY_LEVEL = 160
|
||||
GO
|
||||
-- Pokud je AUTO_CLOSE zapnuté (ON), databáze se po odpojení posledního uživatele kompletně uzavře a uvolní paměť. Při dalším požadavku se pak musí znovu otevírat, což způsobuje výrazné zpoždění (latenci) prvního dotazu a zbytečnou zátěž na disk a CPU.
|
||||
ALTER DATABASE [kufrik_master] SET AUTO_CLOSE OFF WITH NO_WAIT;
|
||||
GO
|
||||
|
||||
-- user:
|
||||
USE [master]
|
||||
GO
|
||||
CREATE LOGIN [kufrik] WITH PASSWORD=N'giteaKufrik_2026!', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
|
||||
GO
|
||||
USE [kufrik_master]
|
||||
GO
|
||||
CREATE USER [kufrik] FOR LOGIN [kufrik]
|
||||
GO
|
||||
ALTER USER [kufrik] WITH DEFAULT_SCHEMA=[dbo]
|
||||
GO
|
||||
ALTER ROLE [db_owner] ADD MEMBER [kufrik]
|
||||
GO
|
||||
|
||||
-- TABULKA LOGIN
|
||||
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LOGIN]') AND type in (N'U'))
|
||||
BEGIN
|
||||
CREATE TABLE [dbo].[LOGIN] (
|
||||
[ID] [int] IDENTITY(1,1) NOT NULL,
|
||||
[EMAIL] [nvarchar](100) NOT NULL,
|
||||
[HESLO_HASH] [nvarchar](255) NOT NULL,
|
||||
[POSLEDNI_PRIHLASENI] [datetime2](7) NULL,
|
||||
[AKTIVNI] [bit] NOT NULL CONSTRAINT [DF_LOGIN_AKTIVNI] DEFAULT 1,
|
||||
[DATABASE] [nvarchar](128) NOT NULL,
|
||||
[SERVER] [nvarchar](100) NOT NULL,
|
||||
[FIRMA_ID] [int] NOT NULL, -- vazna na tabulku FIRMA v datové DB
|
||||
|
||||
CONSTRAINT [PK_LOGIN] PRIMARY KEY CLUSTERED ([ID] ASC),
|
||||
CONSTRAINT [UQ_LOGIN_EMAIL] UNIQUE NONCLUSTERED ([EMAIL] ASC)
|
||||
);
|
||||
END
|
||||
GO
|
||||
|
||||
SET IDENTITY_INSERT dbo.[LOGIN] ON;
|
||||
INSERT INTO dbo.[LOGIN] (
|
||||
[ID],
|
||||
[EMAIL],
|
||||
[HESLO_HASH],
|
||||
[DATABASE],
|
||||
[SERVER],
|
||||
[FIRMA_ID]
|
||||
)
|
||||
VALUES (
|
||||
1,
|
||||
N'admin@kmetix.cz',
|
||||
-- Heslo 'admin1234' (platný BCrypt hash pro Nette Passwords)
|
||||
N'$2y$12$9SAWctZKp/JpYl7K.awSRehXOXVyRpu0ofReqvbm1jVjvdz41zH7a',
|
||||
N'kufrik',
|
||||
N'localhost',
|
||||
1 -- Odkaz na FIRMA.ID = 1 v klientské databázi 'kufrik'
|
||||
);
|
||||
SET IDENTITY_INSERT dbo.[LOGIN] OFF;
|
||||
GO
|
||||
|
||||
-- ==========================================
|
||||
-- DATOVÁ databáze
|
||||
-- ==========================================
|
||||
|
||||
CREATE DATABASE [kufrik]
|
||||
-- CONTAINMENT = NONE
|
||||
|
|
@ -13,16 +85,11 @@ CREATE DATABASE [kufrik]
|
|||
GO
|
||||
ALTER DATABASE [kufrik] SET COMPATIBILITY_LEVEL = 160
|
||||
GO
|
||||
|
||||
--Pokud je AUTO_CLOSE zapnuté (ON), databáze se po odpojení posledního uživatele kompletně uzavře a uvolní paměť. Při dalším požadavku se pak musí znovu otevírat, což způsobuje výrazné zpoždění (latenci) prvního dotazu a zbytečnou zátěž na disk a CPU.
|
||||
ALTER DATABASE [kufrik] SET AUTO_CLOSE OFF WITH NO_WAIT;
|
||||
GO
|
||||
|
||||
--user:
|
||||
USE [master]
|
||||
GO
|
||||
CREATE LOGIN [kufrik] WITH PASSWORD=N'giteaKufrik_2026!', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
|
||||
GO
|
||||
--user (stejný i pro master DB):
|
||||
USE [kufrik]
|
||||
GO
|
||||
CREATE USER [kufrik] FOR LOGIN [kufrik]
|
||||
|
|
@ -32,40 +99,25 @@ GO
|
|||
ALTER ROLE [db_owner] ADD MEMBER [kufrik]
|
||||
GO
|
||||
|
||||
-- 1. PŘIHLAŠOVACÍ ÚČET (Fyzická osoba)
|
||||
CREATE TABLE [dbo].[UZIVATEL] (
|
||||
[ID] [int] IDENTITY(1,1) NOT NULL,
|
||||
[EMAIL] [nvarchar](100) NOT NULL,
|
||||
[HESLO_HASH] [nvarchar](255) NOT NULL,
|
||||
[JMENO] [nvarchar](100) NULL,
|
||||
[PRIJMENI] [nvarchar](100) NULL,
|
||||
[TELEFON] [nvarchar](20) NULL,
|
||||
[POSLEDNI_PRIHLASENI] [datetime2](7) NULL,
|
||||
[AKTIVNI] [bit] NOT NULL DEFAULT 1,
|
||||
CONSTRAINT [PK_UZIVATEL] PRIMARY KEY CLUSTERED ([ID] ASC),
|
||||
CONSTRAINT [UQ_UZIVATEL_EMAIL] UNIQUE ([EMAIL])
|
||||
);
|
||||
-- ==========================================
|
||||
-- 1. FIRMA HLAVNÍ TABULKA - vázaná na LOGIN z master_DB (taky bude tenant - izolace dat)
|
||||
-- ==========================================
|
||||
|
||||
ALTER TABLE [dbo].[UZIVATEL] ADD
|
||||
-- Identifikátor nebo název cílové databáze
|
||||
[DATABASE_NAME] [nvarchar](128) NULL,
|
||||
-- Volitelně celý connection string (nebo klíč do konfigurace aplikace)
|
||||
[CONNECTION_STRING_KEY] [nvarchar](100) NULL;
|
||||
GO
|
||||
|
||||
-- 2. FIRMA / OSVČ (Fakturační jednotka = TENANT)
|
||||
CREATE TABLE [dbo].[FIRMA] (
|
||||
[ID] [int] IDENTITY(1,1) NOT NULL,
|
||||
[NAZEV] [nvarchar](255) NOT NULL, -- Jméno a příjmení OSVČ nebo název s.r.o.
|
||||
[IC] [nvarchar](8) NULL,
|
||||
[DIC] [nvarchar](12) NULL,
|
||||
[PLATCE_DPH] [bit] NOT NULL DEFAULT 1,
|
||||
[PLATCE_DPH] [bit] NOT NULL DEFAULT 0,
|
||||
|
||||
-- Kontaktní údaje
|
||||
[TELEFON] [nvarchar](20) NULL,
|
||||
[EMAIL] [nvarchar](100) NULL,
|
||||
|
||||
-- Sídlo
|
||||
[ULICE] [nvarchar](100) NULL,
|
||||
[MESTO] [nvarchar](100) NULL,
|
||||
[PSC] [nvarchar](10) NULL,
|
||||
[ZEME_KOD] [nvarchar](2) NOT NULL DEFAULT 'CZ',
|
||||
|
||||
-- Banka pro fakturaci
|
||||
[CISLO_UCTU] [nvarchar](30) NULL,
|
||||
|
|
@ -73,19 +125,8 @@ CREATE TABLE [dbo].[FIRMA] (
|
|||
[IBAN] [nvarchar](34) NULL,
|
||||
[BIC] [nvarchar](11) NULL,
|
||||
|
||||
[DATUM_VYTVORENI] [datetime2](7) NOT NULL DEFAULT SYSDATETIME(),
|
||||
CONSTRAINT [PK_FIRMA] PRIMARY KEY CLUSTERED ([ID] ASC)
|
||||
);
|
||||
|
||||
-- 3. VAZBA UŽIVATEL <-> FIRMA (Práva k firmám)
|
||||
CREATE TABLE [dbo].[UZIVATEL_FIRMA] (
|
||||
[UZIVATEL_ID] [int] NOT NULL,
|
||||
[FIRMA_ID] [int] NOT NULL,
|
||||
[ROLE] [nvarchar](20) NOT NULL DEFAULT 'OWNER', -- 'OWNER', 'ACCOUNTANT', 'READONLY'
|
||||
CONSTRAINT [PK_UZIVATEL_FIRMA] PRIMARY KEY CLUSTERED ([UZIVATEL_ID], [FIRMA_ID]),
|
||||
CONSTRAINT [FK_UF_UZIVATEL] FOREIGN KEY ([UZIVATEL_ID]) REFERENCES [dbo].[UZIVATEL] ([ID]),
|
||||
CONSTRAINT [FK_UF_FIRMA] FOREIGN KEY ([FIRMA_ID]) REFERENCES [dbo].[FIRMA] ([ID])
|
||||
);
|
||||
|
||||
-- ==========================================
|
||||
-- 1. ZÁKAZNÍCI / ODBĚRATELÉ (vázaní na Firmu)
|
||||
|
|
@ -240,41 +281,47 @@ GO
|
|||
|
||||
|
||||
-- ==========================================
|
||||
-- Cvičná data a UZIVATEL
|
||||
-- Cvičná data FIRMA
|
||||
-- ==========================================
|
||||
|
||||
-- Vygenerovaný platný BCrypt hash (cost: 12) pro heslo: admin1234
|
||||
DECLARE @HesloHash NVARCHAR(255) = N'$2y$12$9SAWctZKp/JpYl7K.awSRehXOXVyRpu0ofReqvbm1jVjvdz41zH7a';
|
||||
DECLARE @Email NVARCHAR(100) = N'admin@kmetix.cz';
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.UZIVATEL WHERE EMAIL = @Email)
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.[FIRMA] WHERE [ID] = 1)
|
||||
BEGIN
|
||||
INSERT INTO dbo.UZIVATEL (
|
||||
[EMAIL],
|
||||
[HESLO_HASH],
|
||||
[JMENO],
|
||||
[PRIJMENI],
|
||||
SET IDENTITY_INSERT dbo.[FIRMA] ON;
|
||||
|
||||
INSERT INTO dbo.[FIRMA] (
|
||||
[ID],
|
||||
[NAZEV],
|
||||
[IC],
|
||||
[DIC],
|
||||
[PLATCE_DPH],
|
||||
[TELEFON],
|
||||
[DATABASE_NAME],
|
||||
[CONNECTION_STRING_KEY],
|
||||
[AKTIVNI]
|
||||
[EMAIL],
|
||||
[ULICE],
|
||||
[MESTO],
|
||||
[PSC],
|
||||
[CISLO_UCTU],
|
||||
[BANKA_KOD],
|
||||
[IBAN],
|
||||
[BIC]
|
||||
)
|
||||
VALUES (
|
||||
@Email,
|
||||
@HesloHash,
|
||||
N'Pavel',
|
||||
N'Kowalski',
|
||||
1,
|
||||
N'Pavel Kowalski - Kmetix Systems',
|
||||
N'12345678',
|
||||
N'CZ12345678',
|
||||
1,
|
||||
N'+420123456789',
|
||||
N'', -- Cílová databáze pro klientská data
|
||||
NULL,
|
||||
1 -- AKTIVNI = 1
|
||||
N'admin@kmetix.cz',
|
||||
N'Nádražní 123',
|
||||
N'Liberec',
|
||||
N'46001',
|
||||
N'123456789/0800',
|
||||
N'0800',
|
||||
N'CZ6508000000000123456789',
|
||||
N'GIGACZPX'
|
||||
);
|
||||
|
||||
PRINT 'Uživatel admin@kmetix.cz byl úspěšně vytvořen s heslem: admin1234';
|
||||
SET IDENTITY_INSERT dbo.[FIRMA] OFF;
|
||||
PRINT N'Cvičná firma ID 1 byla úspěšně vložena do klientské DB.';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
PRINT 'Uživatel s tímto e-mailem již v databázi existuje.';
|
||||
END
|
||||
GO
|
||||
|
||||
GO
|
||||
Loading…
Reference in New Issue