Filtr faktur.

master
Kowalski 2026-09-01 18:35:12 +02:00
parent dfd1305ad2
commit 7a98fb3f53
4 changed files with 176 additions and 46 deletions

View File

@ -24,9 +24,10 @@ class FakturaFacade
$params[] = $rok; $params[] = $rok;
} }
if ($mesic !== '') { $mesicCislo = (int) $mesic;
if ($mesic !== '' && $mesicCislo >= 1 && $mesicCislo <= 12) {
$where[] = 'MONTH(F.DATUM_VYSTAVENI) = ?'; $where[] = 'MONTH(F.DATUM_VYSTAVENI) = ?';
$params[] = (int) $mesic; $params[] = $mesicCislo;
} }
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : ''; $whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
@ -43,6 +44,35 @@ class FakturaFacade
return $this->kufrikExplorer->query($sql, ...$params)->fetchAll(); return $this->kufrikExplorer->query($sql, ...$params)->fetchAll();
} }
/**
* Roky, pro které existují faktury (plus aktuální / právě vybraný rok).
*
* @return int[]
*/
public function getDostupneRoky(?int $vybranyRok = null): array
{
$rows = $this->kufrikExplorer->query("
SELECT DISTINCT YEAR(DATUM_VYSTAVENI) AS ROK
FROM dbo.FAKTURA
WHERE DATUM_VYSTAVENI IS NOT NULL
ORDER BY ROK DESC
")->fetchAll();
$roky = [];
foreach ($rows as $row) {
$roky[] = (int) $row->ROK;
}
foreach ([(int) date('Y'), $vybranyRok] as $rok) {
if ($rok && !in_array($rok, $roky, true)) {
$roky[] = $rok;
}
}
rsort($roky, SORT_NUMERIC);
return $roky;
}
/** /**
* Detail jedné faktury podle ID * Detail jedné faktury podle ID
*/ */

View File

@ -135,35 +135,34 @@
</div> </div>
<!-- Filtr pro Faktury v Toolbaru --> <!-- Filtr pro Faktury v Toolbaru -->
<form n:attr="method => 'get', action => $presenter->link('Faktura:default')" class="filter-panel ms-auto ms-lg-0"> <form n:attr="method => 'get', action => $presenter->link('Faktura:default', ['rok' => null, 'mesic' => null])" class="filter-panel ms-auto ms-lg-0">
<i class="bi bi-funnel-fill text-muted fs-5 ms-1"></i> <i class="bi bi-funnel-fill text-muted fs-5 ms-1"></i>
{var $aktualniRok = (int) date('Y')} {var $aktualniRok = (int) date('Y')}
{var $vybranyRok = $presenter->rok ?? $aktualniRok} {var $vybranyRok = $selectedRok ?? $aktualniRok}
{var $vybranyMesic = $presenter->mesic ?? ''} {var $vybranyMesic = (string) ($selectedMesic ?? '')}
{var $rokyFiltr = $roky ?? range($aktualniRok, $aktualniRok - 3)}
<!-- Výběr Roku --> <!-- Výběr Roku -->
<select name="rok" class="form-select form-select-sm" style="width: 90px;" onchange="this.form.submit()"> <select name="rok" class="form-select form-select-sm" style="width: 90px;" onchange="this.form.submit()">
{for $r = $aktualniRok; $r >= $aktualniRok - 3; $r--} <option n:foreach="$rokyFiltr as $r" value="{$r}" {if $vybranyRok == $r}selected{/if}>{$r}</option>
<option value="{$r}" {if $vybranyRok == $r}selected{/if}>{$r}</option>
{/for}
</select> </select>
<!-- Výběr Měsíce --> <!-- Výběr Měsíce -->
<select name="mesic" class="form-select form-select-sm" style="width: 140px;" onchange="this.form.submit()"> <select name="mesic" class="form-select form-select-sm" style="width: 140px;" onchange="this.form.submit()">
<option value="" {if $vybranyMesic === ''}selected{/if}>(všechny měsíce)</option> <option value="" {if $vybranyMesic === ''}selected{/if}>(všechny měsíce)</option>
<option value="1" {if $vybranyMesic === '1'}selected{/if}>Leden</option> <option value="1" {if $vybranyMesic == '1'}selected{/if}>Leden</option>
<option value="2" {if $vybranyMesic === '2'}selected{/if}>Únor</option> <option value="2" {if $vybranyMesic == '2'}selected{/if}>Únor</option>
<option value="3" {if $vybranyMesic === '3'}selected{/if}>Březen</option> <option value="3" {if $vybranyMesic == '3'}selected{/if}>Březen</option>
<option value="4" {if $vybranyMesic === '4'}selected{/if}>Duben</option> <option value="4" {if $vybranyMesic == '4'}selected{/if}>Duben</option>
<option value="5" {if $vybranyMesic === '5'}selected{/if}>Květen</option> <option value="5" {if $vybranyMesic == '5'}selected{/if}>Květen</option>
<option value="6" {if $vybranyMesic === '6'}selected{/if}>Červen</option> <option value="6" {if $vybranyMesic == '6'}selected{/if}>Červen</option>
<option value="7" {if $vybranyMesic === '7'}selected{/if}>Červenec</option> <option value="7" {if $vybranyMesic == '7'}selected{/if}>Červenec</option>
<option value="8" {if $vybranyMesic === '8'}selected{/if}>Srpen</option> <option value="8" {if $vybranyMesic == '8'}selected{/if}>Srpen</option>
<option value="9" {if $vybranyMesic === '9'}selected{/if}>Září</option> <option value="9" {if $vybranyMesic == '9'}selected{/if}>Září</option>
<option value="10" {if $vybranyMesic === '10'}selected{/if}>Říjen</option> <option value="10" {if $vybranyMesic == '10'}selected{/if}>Říjen</option>
<option value="11" {if $vybranyMesic === '11'}selected{/if}>Listopad</option> <option value="11" {if $vybranyMesic == '11'}selected{/if}>Listopad</option>
<option value="12" {if $vybranyMesic === '12'}selected{/if}>Prosinec</option> <option value="12" {if $vybranyMesic == '12'}selected{/if}>Prosinec</option>
</select> </select>
</form> </form>
</div> </div>

View File

@ -4,29 +4,60 @@ namespace App\UI\Faktura;
use App\UI\BasePresenter; use App\UI\BasePresenter;
use App\Model\Facade\FakturaFacade; use App\Model\Facade\FakturaFacade;
use Nette\Application\Attributes\Persistent;
class FakturaPresenter extends BasePresenter class FakturaPresenter extends BasePresenter
{ {
/** @persistent */ private const FILTER_COOKIE = 'kufrikFakturaFiltr';
public int $rok; private const FILTER_SESSION = 'fakturaFilter';
/** @persistent */ #[Persistent]
public ?int $rok = null;
#[Persistent]
public string $mesic = ''; public string $mesic = '';
public function __construct( public function __construct(
private FakturaFacade $fakturaFacade private FakturaFacade $fakturaFacade
) { ) {
parent::__construct(); parent::__construct();
$this->rok = (int) date('Y'); }
protected function startup(): void
{
parent::startup();
$query = $this->getHttpRequest()->getQuery();
$fromUrl = array_key_exists('rok', $query) || array_key_exists('mesic', $query);
if ($fromUrl) {
$this->rok = $this->rok ?? (int) date('Y');
$this->mesic = $this->normalizeMesic($this->mesic);
$this->saveFilter($this->rok, $this->mesic);
return;
}
$saved = $this->loadFilter();
if ($saved === null) {
return;
}
$this->rok = $saved['rok'];
$this->mesic = $saved['mesic'];
// Do URL doplníme uložený filtr, ale ne při signálu (úhrada / smazání).
if ($this->getSignal() === null) {
$this->redirect('this');
}
} }
public function renderDefault(): void public function renderDefault(): void
{ {
// RLS v databázi se postará o filtraci podle firmy, $rok = $this->rok ?? (int) date('Y');
// fasáda vrátí seřazené faktury s dopočítanou sumou. $mesic = $this->normalizeMesic($this->mesic);
$faktury = $this->fakturaFacade->getFiltered($this->rok, $this->mesic);
$faktury = $this->fakturaFacade->getFiltered($rok, $mesic);
// Agregace pro přehledové karty nad aktuálním výběrem
$celkemSum = 0; $celkemSum = 0;
$neuhrazenoCount = 0; $neuhrazenoCount = 0;
@ -40,8 +71,79 @@ class FakturaPresenter extends BasePresenter
$this->template->faktury = $faktury; $this->template->faktury = $faktury;
$this->template->celkemSum = $celkemSum; $this->template->celkemSum = $celkemSum;
$this->template->neuhrazenoCount = $neuhrazenoCount; $this->template->neuhrazenoCount = $neuhrazenoCount;
$this->template->selectedRok = $this->rok ?? (int) date('Y'); // <--- PŘIDÁNO $this->template->selectedRok = $rok;
$this->template->selectedMesic = $this->mesic ?? ''; // <--- PŘIDÁNO $this->template->selectedMesic = $mesic;
$this->template->roky = $this->fakturaFacade->getDostupneRoky($rok);
}
private function normalizeMesic(string $mesic): string
{
if ($mesic === '') {
return '';
}
$cislo = (int) $mesic;
return ($cislo >= 1 && $cislo <= 12) ? (string) $cislo : '';
}
/**
* @return array{rok: int, mesic: string}|null
*/
private function loadFilter(): ?array
{
$uid = (int) $this->getUser()->getId();
$section = $this->getSession(self::FILTER_SESSION);
if (isset($section->rok) && (int) ($section->userId ?? 0) === $uid) {
return $this->validFilter((int) $section->rok, (string) ($section->mesic ?? ''));
}
$raw = $this->getHttpRequest()->getCookie(self::FILTER_COOKIE);
if (!is_string($raw) || $raw === '') {
return null;
}
$parts = explode(':', $raw, 3);
if (count($parts) < 2 || (int) $parts[0] !== $uid) {
return null;
}
return $this->validFilter((int) $parts[1], (string) ($parts[2] ?? ''));
}
private function saveFilter(int $rok, string $mesic): void
{
$uid = (int) $this->getUser()->getId();
$section = $this->getSession(self::FILTER_SESSION);
$section->userId = $uid;
$section->rok = $rok;
$section->mesic = $mesic;
$this->getHttpResponse()->setCookie(
self::FILTER_COOKIE,
$uid . ':' . $rok . ':' . $mesic,
'1 year',
null,
null,
null,
true,
'Lax'
);
}
/**
* @return array{rok: int, mesic: string}|null
*/
private function validFilter(int $rok, string $mesic): ?array
{
if ($rok < 2000 || $rok > 2100) {
return null;
}
return [
'rok' => $rok,
'mesic' => $this->normalizeMesic($mesic),
];
} }
/** /**

View File

@ -30,31 +30,30 @@
<!-- Filtrační lišta (Rok / Měsíc) --> <!-- Filtrační lišta (Rok / Měsíc) -->
<div class="card p-3 mb-4 border-0 shadow-sm bg-light"> <div class="card p-3 mb-4 border-0 shadow-sm bg-light">
<form method="get" class="row g-2 align-items-center"> <form n:attr="method => 'get', action => $presenter->link('this', ['rok' => null, 'mesic' => null])" class="row g-2 align-items-center">
<div class="col-auto"> <div class="col-auto">
<label class="col-form-label fw-bold">Filtr roku/měsíce:</label> <label class="col-form-label fw-bold">Filtr roku/měsíce:</label>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<select name="rok" class="form-select form-select-sm" onchange="this.form.submit()"> <select name="rok" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="2026" {if ($vybranyRok ?? date('Y')) == 2026}selected{/if}>2026</option> <option n:foreach="$roky as $r" value="{$r}" {if $selectedRok == $r}selected{/if}>{$r}</option>
<option value="2025" {if ($vybranyRok ?? date('Y')) == 2025}selected{/if}>2025</option>
</select> </select>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<select name="mesic" class="form-select form-select-sm" onchange="this.form.submit()"> <select name="mesic" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">-- Všechny měsíce --</option> <option value="" {if $selectedMesic === ''}selected{/if}>-- Všechny měsíce --</option>
<option value="1" {if ($vybranyMesic ?? '') == '1'}selected{/if}>Leden</option> <option value="1" {if $selectedMesic == '1'}selected{/if}>Leden</option>
<option value="2" {if ($vybranyMesic ?? '') == '2'}selected{/if}>Únor</option> <option value="2" {if $selectedMesic == '2'}selected{/if}>Únor</option>
<option value="3" {if ($vybranyMesic ?? '') == '3'}selected{/if}>Březen</option> <option value="3" {if $selectedMesic == '3'}selected{/if}>Březen</option>
<option value="4" {if ($vybranyMesic ?? '') == '4'}selected{/if}>Duben</option> <option value="4" {if $selectedMesic == '4'}selected{/if}>Duben</option>
<option value="5" {if ($vybranyMesic ?? '') == '5'}selected{/if}>Květen</option> <option value="5" {if $selectedMesic == '5'}selected{/if}>Květen</option>
<option value="6" {if ($vybranyMesic ?? '') == '6'}selected{/if}>Červen</option> <option value="6" {if $selectedMesic == '6'}selected{/if}>Červen</option>
<option value="7" {if ($vybranyMesic ?? '') == '7'}selected{/if}>Červenec</option> <option value="7" {if $selectedMesic == '7'}selected{/if}>Červenec</option>
<option value="8" {if ($vybranyMesic ?? '') == '8'}selected{/if}>Srpen</option> <option value="8" {if $selectedMesic == '8'}selected{/if}>Srpen</option>
<option value="9" {if ($vybranyMesic ?? '') == '9'}selected{/if}>Září</option> <option value="9" {if $selectedMesic == '9'}selected{/if}>Září</option>
<option value="10" {if ($vybranyMesic ?? '') == '10'}selected{/if}>Říjen</option> <option value="10" {if $selectedMesic == '10'}selected{/if}>Říjen</option>
<option value="11" {if ($vybranyMesic ?? '') == '11'}selected{/if}>Listopad</option> <option value="11" {if $selectedMesic == '11'}selected{/if}>Listopad</option>
<option value="12" {if ($vybranyMesic ?? '') == '12'}selected{/if}>Prosinec</option> <option value="12" {if $selectedMesic == '12'}selected{/if}>Prosinec</option>
</select> </select>
</div> </div>
</form> </form>