diff --git a/Kufřík DEPLOY.bat b/Kufřík DEPLOY.bat new file mode 100644 index 0000000..9e18c78 --- /dev/null +++ b/Kufřík DEPLOY.bat @@ -0,0 +1,25 @@ +@echo off +title Deploying Kufrik to Production... +cd /d "%~dp0" + +echo ======================================== +echo Spoustim deployment na produkcni server... +echo ======================================== +echo. + +php vendor/bin/dep deploy production + +if %ERRORLEVEL% EQU 0 ( + echo. + echo ======================================== + echo Hotovo! Deployment probehl uspesne. + echo ======================================== +) else ( + echo. + echo ======================================== + echo CHYBA: Deployment selhal! + echo ======================================== +) + +echo. +pause \ No newline at end of file diff --git a/app/Model/Facade/FakturaFacade.php b/app/Model/Facade/FakturaFacade.php index 8cd994f..3dbcc7e 100644 --- a/app/Model/Facade/FakturaFacade.php +++ b/app/Model/Facade/FakturaFacade.php @@ -244,78 +244,140 @@ class FakturaFacade } /** - * Uložení / Aktualizace hlavičky faktury + vytvoření snapshotů D_* a O_* + * Snapshot dodavatele z aktuálních údajů firmy (výchozí hodnoty nové faktury). + * + * @return array + */ + public function getDodavatelDefaults(int $firmaId): array + { + $firma = $this->kufrikExplorer->table('FIRMA')->get($firmaId); + if (!$firma) { + return []; + } + + return [ + 'D_PRAJM' => $firma->NAZEV ?? null, + 'D_ULICE' => $firma->ULICE ?? null, + 'D_MESTO' => $firma->MESTO ?? null, + 'D_PSC' => $firma->PSC ?? null, + 'D_IC' => $firma->IC ?? null, + 'D_DIC' => $firma->DIC ?? null, + 'D_BANKA' => self::formatFirmaUcet($firma), + 'D_TELEFON' => $firma->TELEFON ?? null, + 'D_EMAIL' => $firma->EMAIL ?? null, + 'D_WEB' => self::firmaWeb($firma), + ]; + } + + /** + * Uložení / Aktualizace hlavičky faktury + snapshotů D_* a O_* */ public function saveFaktura(array $data, ?int $id = null, int $firmaId): int { + $snapshot = $this->resolveSnapshot($data, $firmaId, $id === null); + if ($id) { - // PŘI EDITACI: Mění se pouze měnitelné údaje (splatnost, DUZP, stav, činnosti) $values = [ 'DATUM_SPLATNOSTI' => $data['DATUM_SPLATNOSTI'], 'DATUM_DUZP' => !empty($data['DATUM_DUZP']) ? $data['DATUM_DUZP'] : null, + 'FORMA_UHRADY' => (int) ($data['FORMA_UHRADY'] ?? 1), 'CINNOSTI' => !empty($data['CINNOSTI']) ? $data['CINNOSTI'] : null, - ]; + ] + $snapshot; $this->kufrikExplorer->table('FAKTURA')->where('ID', $id)->update($values); return $id; + } - } else { - // PŘI NOVÉ FAKTUŘE: Ukládáme vše (včetně DATUM_VYSTAVENI, CISLO, ZAKAZNIK a snapshotů) - $firma = $this->kufrikExplorer->table('FIRMA')->where('ID', $firmaId)->fetch(); - $zakaznikId = !empty($data['ZAKAZNIK_ID']) ? (int) $data['ZAKAZNIK_ID'] : null; - $zakaznik = $zakaznikId ? $this->kufrikExplorer->table('ZAKAZNIK')->where('ID', $zakaznikId)->fetch() : null; + $zakaznikId = !empty($data['ZAKAZNIK_ID']) ? (int) $data['ZAKAZNIK_ID'] : null; + if (!$zakaznikId) { + throw new \InvalidArgumentException('Faktura musí mít odběratele.'); + } - if (!$zakaznikId || !$zakaznik) { - throw new \InvalidArgumentException('Faktura musí mít odběratele.'); + $values = [ + 'FIRMA_ID' => $firmaId, + 'ZAKAZNIK' => $zakaznikId, + 'CISLO' => $data['CISLO'], + 'CISLO_ABS' => $this->resolveCisloAbs($data, $firmaId), + 'FORMA_UHRADY' => (int) ($data['FORMA_UHRADY'] ?? 1), + 'DATUM_VYSTAVENI' => $data['DATUM_VYSTAVENI'], + 'DATUM_SPLATNOSTI' => $data['DATUM_SPLATNOSTI'], + 'DATUM_DUZP' => !empty($data['DATUM_DUZP']) ? $data['DATUM_DUZP'] : null, + 'STAV' => $data['STAV'] ?? 'ISSUED', + 'CINNOSTI' => !empty($data['CINNOSTI']) ? $data['CINNOSTI'] : null, + ] + $snapshot; + + $row = $this->kufrikExplorer->table('FAKTURA')->insert($values); + return $row->ID; + } + + /** + * Snapshot z formuláře; u nové faktury doplní chybějící údaje z firmy / zákazníka. + * + * @return array + */ + private function resolveSnapshot(array $data, int $firmaId, bool $isNew): array + { + $fromForm = [ + 'D_PRAJM' => self::nullIfEmpty($data['D_PRAJM'] ?? null), + 'D_ULICE' => self::nullIfEmpty($data['D_ULICE'] ?? null), + 'D_MESTO' => self::nullIfEmpty($data['D_MESTO'] ?? null), + 'D_PSC' => self::nullIfEmpty($data['D_PSC'] ?? null), + 'D_IC' => self::nullIfEmpty($data['D_IC'] ?? null), + 'D_DIC' => self::nullIfEmpty($data['D_DIC'] ?? null), + 'D_BANKA' => self::nullIfEmpty($data['D_BANKA'] ?? null), + 'D_TELEFON' => self::nullIfEmpty($data['D_TELEFON'] ?? null), + 'D_EMAIL' => self::nullIfEmpty($data['D_EMAIL'] ?? null), + 'D_WEB' => self::nullIfEmpty($data['D_WEB'] ?? null), + 'O_PRAJM' => self::nullIfEmpty($data['O_PRAJM'] ?? null), + 'O_ULICE' => self::nullIfEmpty($data['O_ULICE'] ?? null), + 'O_MESTO' => self::nullIfEmpty($data['O_MESTO'] ?? null), + 'O_PSC' => self::nullIfEmpty($data['O_PSC'] ?? null), + 'O_IC' => self::nullIfEmpty($data['O_IC'] ?? null), + 'O_DIC' => self::nullIfEmpty($data['O_DIC'] ?? null), + ]; + + if (!$isNew) { + return $fromForm; + } + + $defaults = $this->getDodavatelDefaults($firmaId); + foreach ($defaults as $key => $value) { + if ($fromForm[$key] === null || $fromForm[$key] === '') { + $fromForm[$key] = $value; } + } - $oPrajm = $zakaznik ? trim(($zakaznik->PRIJMENI ?? '') . ' ' . ($zakaznik->JMENO ?? '')) : null; - - $dBanka = null; - if ($firma) { - if (isset($firma->UCET, $firma->KOD_BANKY) && $firma->UCET) { - $dBanka = $firma->UCET . '/' . $firma->KOD_BANKY; - } elseif (isset($firma->BANKA)) { - $dBanka = $firma->BANKA; - } - } - - $values = [ - 'FIRMA_ID' => $firmaId, - 'ZAKAZNIK' => $zakaznikId, - 'CISLO' => $data['CISLO'], - 'CISLO_ABS' => $this->resolveCisloAbs($data, $firmaId), - 'FORMA_UHRADY' => (int) ($data['FORMA_UHRADY'] ?? 1), - 'DATUM_VYSTAVENI' => $data['DATUM_VYSTAVENI'], - 'DATUM_SPLATNOSTI' => $data['DATUM_SPLATNOSTI'], - 'DATUM_DUZP' => !empty($data['DATUM_DUZP']) ? $data['DATUM_DUZP'] : null, - 'STAV' => $data['STAV'] ?? 'ISSUED', - 'CINNOSTI' => !empty($data['CINNOSTI']) ? $data['CINNOSTI'] : null, - - // SNAPSHOT DODAVATELE - 'D_PRAJM' => $firma->NAZEV ?? null, - 'D_ULICE' => $firma->ULICE ?? null, - 'D_MESTO' => $firma->MESTO ?? null, - 'D_PSC' => $firma->PSC ?? null, - 'D_IC' => $firma->IC ?? null, - 'D_DIC' => $firma->DIC ?? null, - 'D_BANKA' => $dBanka, - 'D_TELEFON' => $firma->TELEFON ?? null, - 'D_EMAIL' => $firma->EMAIL ?? null, - 'D_WEB' => $firma->WEB ?? null, - - // SNAPSHOT ODBĚRATELE - 'O_PRAJM' => $oPrajm, + $zakaznikId = !empty($data['ZAKAZNIK_ID']) ? (int) $data['ZAKAZNIK_ID'] : null; + $zakaznik = $zakaznikId + ? $this->kufrikExplorer->table('ZAKAZNIK')->where('ID', $zakaznikId)->fetch() + : null; + if ($zakaznik) { + $oDefaults = [ + 'O_PRAJM' => trim(($zakaznik->PRIJMENI ?? '') . ' ' . ($zakaznik->JMENO ?? '')) ?: null, 'O_ULICE' => $zakaznik->ULICE ?? null, 'O_MESTO' => $zakaznik->MESTO ?? null, 'O_PSC' => $zakaznik->PSC ?? null, 'O_IC' => $zakaznik->IC ?? null, 'O_DIC' => $zakaznik->DIC ?? null, ]; - - $row = $this->kufrikExplorer->table('FAKTURA')->insert($values); - return $row->ID; + foreach ($oDefaults as $key => $value) { + if ($fromForm[$key] === null || $fromForm[$key] === '') { + $fromForm[$key] = $value; + } + } } + + return $fromForm; + } + + private static function nullIfEmpty(mixed $value): ?string + { + if ($value === null) { + return null; + } + $text = trim((string) $value); + + return $text !== '' ? $text : null; } /** @@ -568,6 +630,23 @@ class FakturaFacade 'DATUM_DUZP' => $datumVystaveni, 'STAV' => self::STAV_VYSTAVENO, 'CINNOSTI' => $zdroj->CINNOSTI, + // Zachovat upravené údaje dodavatele / odběratele ze zdroje + 'D_PRAJM' => $zdroj->D_PRAJM, + 'D_ULICE' => $zdroj->D_ULICE, + 'D_MESTO' => $zdroj->D_MESTO, + 'D_PSC' => $zdroj->D_PSC, + 'D_IC' => $zdroj->D_IC, + 'D_DIC' => $zdroj->D_DIC, + 'D_BANKA' => $zdroj->D_BANKA, + 'D_TELEFON' => $zdroj->D_TELEFON, + 'D_EMAIL' => $zdroj->D_EMAIL, + 'D_WEB' => $zdroj->D_WEB, + 'O_PRAJM' => $zdroj->O_PRAJM, + 'O_ULICE' => $zdroj->O_ULICE, + 'O_MESTO' => $zdroj->O_MESTO, + 'O_PSC' => $zdroj->O_PSC, + 'O_IC' => $zdroj->O_IC, + 'O_DIC' => $zdroj->O_DIC, ], null, $firmaId); $polozky = []; @@ -585,4 +664,34 @@ class FakturaFacade return $novaId; } + + /** + * Číslo účtu dodavatele ve formátu „účet/kód banky“. + */ + private static function formatFirmaUcet(?object $firma): ?string + { + if (!$firma) { + return null; + } + + $ucet = trim((string) ($firma->CISLO_UCTU ?? '')); + $kod = trim((string) ($firma->BANKA_KOD ?? '')); + if ($ucet !== '') { + return $kod !== '' ? $ucet . '/' . $kod : $ucet; + } + + return null; + } + + private static function firmaWeb(?object $firma): ?string + { + if (!$firma) { + return null; + } + + $web = trim((string) ($firma->WEB ?? '')); + + return $web !== '' ? $web : null; + } + } \ No newline at end of file diff --git a/app/Model/Sdf/SdfKufrikImporter.php b/app/Model/Sdf/SdfKufrikImporter.php index 650b5cc..3868345 100644 --- a/app/Model/Sdf/SdfKufrikImporter.php +++ b/app/Model/Sdf/SdfKufrikImporter.php @@ -65,6 +65,7 @@ class SdfKufrikImporter $nastaveniImported = $this->importNastaveni($explorer, $data['konfigurace'], $firmaId); $zakaznikMap = $this->importZakaznici($explorer, $data['zakaznik'], $firmaId); $fakturaResult = $this->importFaktury($explorer, $data, $firmaId, $zakaznikMap); + $this->doplnFirmaWebZFaktur($explorer, $firmaId); $explorer->getConnection()->commit(); } catch (\Throwable $e) { $explorer->getConnection()->rollBack(); @@ -121,6 +122,7 @@ class SdfKufrikImporter 'PLATCE_DPH' => 0, 'TELEFON' => $this->clip($uzivatel['TELEFON'] ?? null, 20), 'EMAIL' => $this->clip($uzivatel['EMAIL'] ?? null, 100), + 'WEB' => $this->clip($uzivatel['WEB'] ?? $uzivatel['D_WEB'] ?? null, 100), 'ULICE' => $this->clip($uzivatel['ULICE'] ?? null, 100), 'MESTO' => $this->clip($uzivatel['MESTO'] ?? null, 100), 'PSC' => $this->clip($uzivatel['PSC'] ?? null, 10), @@ -145,6 +147,30 @@ class SdfKufrikImporter return 'inserted'; } + /** + * Pokud firma nemá WEB, vezme ho z první faktury s vyplněným D_WEB. + */ + private function doplnFirmaWebZFaktur(Explorer $explorer, int $firmaId): void + { + $firma = $explorer->table('FIRMA')->get($firmaId); + if (!$firma || trim((string) ($firma->WEB ?? '')) !== '') { + return; + } + + $web = $explorer->table('FAKTURA') + ->where('FIRMA_ID', $firmaId) + ->where('D_WEB NOT', null) + ->where('D_WEB NOT', '') + ->order('ID ASC') + ->limit(1) + ->fetchField('D_WEB'); + + $web = $this->clip($web, 100); + if ($web !== null) { + $explorer->table('FIRMA')->where('ID', $firmaId)->update(['WEB' => $web]); + } + } + /** * Maska, období číslování a splatnost ze staré tabulky KONFIGURACE. * diff --git a/app/UI/FakturaEdit/FakturaEditPresenter.php b/app/UI/FakturaEdit/FakturaEditPresenter.php index 5000914..5852c97 100644 --- a/app/UI/FakturaEdit/FakturaEditPresenter.php +++ b/app/UI/FakturaEdit/FakturaEditPresenter.php @@ -24,16 +24,17 @@ class FakturaEditPresenter extends BasePresenter $this->id = null; $this->setView('edit'); - $firmaId = $this->getUser()->getIdentity()->firma_id; + $firmaId = (int) $this->getUser()->getIdentity()->firma_id; $dalsi = $this->fakturaFacade->getNextCislo($firmaId); $this['fakturaForm']->setDefaults([ 'CISLO' => $dalsi['cislo'], 'CISLO_ABS' => $dalsi['cisloAbs'], + 'FORMA_UHRADY' => 1, 'DATUM_SPLATNOSTI' => (new \DateTimeImmutable('today')) ->modify('+' . $dalsi['splatnostDny'] . ' days') ->format('Y-m-d'), - ]); + ] + $this->fakturaFacade->getDodavatelDefaults($firmaId)); } public function actionEdit(int $id): void @@ -85,7 +86,17 @@ class FakturaEditPresenter extends BasePresenter ->setHtmlAttribute('tabindex', '-1'); } + public function renderNew(): void + { + $this->prepareEditTemplate(); + } + public function renderEdit(?int $id = null): void + { + $this->prepareEditTemplate(); + } + + private function prepareEditTemplate(): void { $this->template->isEdit = (bool) $this->id; @@ -109,9 +120,14 @@ class FakturaEditPresenter extends BasePresenter } } + $jsonFlags = JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR; $this->template->katalogPolozekJson = json_encode( $this->fakturaFacade->getKatalogPolozek(), - JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR + $jsonFlags + ); + $this->template->zakazniciDetailJson = json_encode( + $this->zakazniciDetailProJs(), + $jsonFlags ); $this->template->polozky = $polozky; $this->template->platby = $platby; @@ -187,9 +203,34 @@ class FakturaEditPresenter extends BasePresenter ->setDefaultValue((new \DateTimeImmutable('today'))->modify('+' . $splatnostDny . ' days')->format('Y-m-d')) ->setRequired('Zadejte datum splatnosti.'); + $form->addSelect('FORMA_UHRADY', 'Forma úhrady:', [ + 1 => 'převodem', + 2 => 'hotově', + ])->setDefaultValue(1); + $form->addText('DATUM_DUZP', 'Datum UZP:') ->setHtmlType('date'); + // Detailní informace — dodavatel + $form->addText('D_PRAJM', 'Dodavatel:')->setMaxLength(511); + $form->addText('D_ULICE', 'Ulice:')->setMaxLength(50); + $form->addText('D_PSC', 'PSČ:')->setMaxLength(50); + $form->addText('D_MESTO', 'Město:')->setMaxLength(50); + $form->addText('D_IC', 'IČ:')->setMaxLength(8); + $form->addText('D_DIC', 'DIČ:')->setMaxLength(10); + $form->addText('D_TELEFON', 'Telefon:')->setMaxLength(15); + $form->addText('D_EMAIL', 'E-mail:')->setMaxLength(100); + $form->addText('D_BANKA', 'Bankovní účet:')->setMaxLength(100); + $form->addText('D_WEB', 'Web:')->setMaxLength(100); + + // Detailní informace — odběratel + $form->addText('O_PRAJM', 'Odběratel:')->setMaxLength(511); + $form->addText('O_ULICE', 'Ulice:')->setMaxLength(50); + $form->addText('O_PSC', 'PSČ:')->setMaxLength(5); + $form->addText('O_MESTO', 'Město:')->setMaxLength(50); + $form->addText('O_IC', 'IČ:')->setMaxLength(8); + $form->addText('O_DIC', 'DIČ:')->setMaxLength(10); + $form->addTextArea('CINNOSTI', 'Činnosti / Popis plnění:'); $form->addSubmit('save', 'Uložit fakturu') @@ -200,6 +241,44 @@ class FakturaEditPresenter extends BasePresenter return $form; } + /** + * @return array + */ + private function zakazniciDetailProJs(): array + { + $map = []; + foreach ($this->zakaznikFacade->getFiltered('', 'aktivni') as $z) { + $map[(string) $z->ID] = [ + 'nazev' => trim(($z->PRIJMENI ?? '') . ' ' . ($z->JMENO ?? '')), + 'ulice' => $z->ULICE, + 'psc' => $z->PSC, + 'mesto' => $z->MESTO, + 'ic' => $z->IC, + 'dic' => $z->DIC, + ]; + } + + if ($this->id) { + $faktura = $this->fakturaFacade->getById($this->id); + $zakaznikId = $faktura ? (int) $faktura->ZAKAZNIK : 0; + if ($zakaznikId && !isset($map[(string) $zakaznikId])) { + $stavajici = $this->zakaznikFacade->getById($zakaznikId); + if ($stavajici) { + $map[(string) $zakaznikId] = [ + 'nazev' => trim(($stavajici->PRIJMENI ?? '') . ' ' . ($stavajici->JMENO ?? '')), + 'ulice' => $stavajici->ULICE, + 'psc' => $stavajici->PSC, + 'mesto' => $stavajici->MESTO, + 'ic' => $stavajici->IC, + 'dic' => $stavajici->DIC, + ]; + } + } + } + + return $map; + } + private function nazevZakaznika(object $zakaznik): string { $nazev = trim(($zakaznik->PRIJMENI ?? '') . ' ' . ($zakaznik->JMENO ?? '')); diff --git a/app/UI/FakturaEdit/edit.latte b/app/UI/FakturaEdit/edit.latte index 671f5ec..7d193c1 100644 --- a/app/UI/FakturaEdit/edit.latte +++ b/app/UI/FakturaEdit/edit.latte @@ -24,21 +24,28 @@ - -
- - + +
+
Základní informace
+
+ + +
+
+ + +
@@ -53,6 +60,105 @@
+ +
+
+ +
+
+
+
+
Dodavatel
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
Odběratel
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+
@@ -209,6 +315,7 @@ document.getElementById('edit-platba-splatnost').addEventListener('click', funct + {syntax off}