Příklad návrhu web aplikace: Tisk a evidence faktur, 1. díl – návrh databáze

Jak jsem psal v komentáři v článku o revoluci web aplikací, chci se v následujících příspěvcích pustit do ukázek z praxe. Napadlo mě, že by mohlo být zajímavé vzít moji první aplikaci, popsat postup, jakým jsem nad ní uvažoval a poukázat na chyby, které s časovým odstupem vidím. Přes návrh databáze, uživatelské rozhraní až po programátorskou teorii. Jestli najdu odvahu, ukážu i to moje čtyři roky staré PHPkové zvěrstvo ;)

Hledáte něco na faktury?

Fakturoid – tisk a správa faktur pro živnostníky a malé firmy. Konečně jednoduše.

Mimochodem, dopadlo to nakonec úplně jinak, než v této sérii článků ;)

Ostatní díly:


  1. 1½. díl – Dokončení návrhu databáze

  2. 2. díl – Uživatelské rozhraní

Uvedu vás do děje. Se dvěma kamarády jsme začátkem roku 2003 začali podnikat jako tři živnostníci. Asi po půl roce ručního vyplňování faktur bylo jasné, že takhle to nepůjde. Jednak já strašně škrábu, ale hlavně jsme potřebovali mít jednoduše přístupný přehled, kdo kolik fakturoval, abychom se mohli podělit o kořist.

Vytvořit webové rozhraní pro tisk faktur a to rychle, rychle

Úkol zněl jasně…

Zadání tedy bylo: Vytvořit webové rozhraní pro vystavování faktur, jejich tisk a evidenci se základními statistikami. Znáte tu poučku, že projekt můžete udělat rychle, levně, dobře – vyberte si dvě? No, tenhle projekt byl pro nás, takže jsem vybral rychle a rychle. Levně se nám transformovalo do rychle, protože každá hodina strávená na tomhle, byla hodina nestrávená na práci, za kterou by někdo zaplatil. Jelikož jsem měl o svých programátorských schopnostech vcelku reálné představy, věděl jsem, že „dobře“ ani nebylo na výběr.

Návrh databáze

Uvažoval jsem asi takhle: Prioritu má tisk faktur. Nejdřív se budu zabývat tím, co je potřeba k tomu, aby z tiskárny vyjela faktura a pak tím ostatním.

K evidenci se dostanu později a budu chtít jen výpis podle data, vědět, kdo fakturu vystavil, komu ji vystavil, na jakou částku a jestli už je zaplacená.

Jaké entity tu mám? Faktury, klienty a nás jako dodavatele.

Jaké náležitosti má faktura: kromě informací o dodavateli a klientovi, které budou v příslušných tabulkách, tu ještě jsou datumy vystavení, splatnosti a zaplacení, text poskytnuté služby a částka.

Co musí obsahovat tabulka Dodavatel? Tady nám stačí jen to, co je nutné pro tisk faktury, protože dodavatelé jsme přece my. Název subjektu, ulice, město, PSČ, IČO, DIČ, číslo účtu a jako bonus jsem tam dal telefon (už nevím proč, asi jsem uvažoval, že by se mohlo číslo vypisovat na faktuře).

Tabulka Klient bude obsáhlejší, protože kromě údajů nutných pro tisk faktury, chci zaznamenat i informace, které by se mohly hodit později, kdybych chtěl na tenhle zárodek napojit nějaký systém správy kontaktů.

Když to propojíme dostaneme databázi, na které nám tisk faktur běží až do dnešních dnů.

Obrázek č. 1 – Celá původní databáze

Opravdu? Já myslím, že ne…

Kolika z vás se asi zatínaly nohy v pěst a kroutily nehty, když jste pročítali předchozí řádky? :) Přiznávám, byla to past a za případnou psychickou újmu se omlouvám. Chtěl jsem ukázat, jak bylo snadné udělat chyby, nad kterými mi dnes zůstává rozum stát. Pojďme se na to mrknout ještě jednou.

Předně je nesmysl oddělovat dodavatele a klienty do dvou tabulek. Jeden důvod je, že obě tabulky by obsahovaly prakticky stejné informace, kdybych neořezal dodavatele, kvůli tomu, že dodavatelé jsme byli jen my a o nás přece nepotřebuji podrobnější informace, ale hlavně budu určitě chtít evidovat i příchozí faktury, kdy někdo fakturuje nás, protože jinak se nedobereme k tak základním statistikám, jako je zisk za čtvrtletí. A co případ, kdy firma je jednou náš klient a jindy náš dodavatel? Pak už je úplně jasné, že jediné rozumné řešení je mít pouze jednu tabulku na subjekty, se kterými jsme v obchodním styku.

Obrázek č. 2 – Upravený vztah mezi fakturou a podnikatelskými subjekty

Druhý zásadní problém je, že do tabulky Faktura, jak jsem ji navrhl výše, nebudeme schopni zaznamenat fakturu, která má více položek. Jediná cesta, jak z toho ven, je vytvořit novou tabulku pro položky faktur. Možná by vás lákalo přímo do ní vložit cizí klíč faktura_id a tím provázat fakturu s položkou, ale to by byla také chyba. Zamysleme se v širších souvislostech. Třeba se rozhodneme později přidat rozhraní, kde budeme evidovat, na čem zrovna v daném projektu děláme a budeme chtít tuhle evidenci použít přímo pro tisk faktury. Pak nevyhnutelně vzniknou úkoly, které ještě nebudou položkami na faktuře a některé z nich tam nebudou nikdy. Potřebujeme tedy vztahovou tabulku invoice_has_task, kde si přiřadíme úkoly k fakturám.

Obrázek č. 3 – Předelaný vztah mezi fakturou a položkami faktury

Když už jsme v tom plánování budoucích rozšíření, bude dobré podchytit do samostatné tabulky i všechny projekty, na nichž pracujeme. Rozhodnutí, jestli vztah mezi úkolem a projektem zachytíme v samostatné tabulce, se bude odvozovat od toho, jestli potřebujeme/dovolíme, aby se jeden úkol vázal na víc projektů. Já jsem se rozhodl zahrát to na jistotu a radši tam vztahovou tabulku dám.

Nad rámec toho, co jsem chtěl dnes stihnout, je úvaha na téma navázání úkolů na pracovníky, propojení projektů se subjekty a mnoho dalších záležitostí, které si můžeme kolem fakturování a práce na položkách faktur vymyslet.

Až když uděláme k modelu nějaké rozhraní, ukáže se, co nás všechno nenapadlo.

Nožičky a závěr

Při návrhu databáze se vždy snažím dívat na daný problém z více stran, snažím se představit si možný budoucí vývoj. Zároveň se mi několikrát potvrdilo, že čím rychleji navržený model prověříme na skutečném nasazení, tím rychleji zjistíme, co jsme nedomysleli. Až když uděláme k modelu nějaké rozhraní, začneme databázi plnit a vypisovat reálná data, teprve pak se ještě ukáže, co nás všechno nenapadlo.

Proto bude příští díl o uživatelském rozhraní, které jsem pro fakturační systém vytvořil předtím a o tom, jak bych ho dělal teď.

Finální model databáze pro jednoduchý fakturační systém vidíte na obrázku č. 4. Pokud by měl někdo zájem, zde je soubor s SQL create příkazy a výsledný soubor DBDesigneru 1. Rozhodně si o sobě nemyslím, že bych byl v návrhu databází nějaký expert, takže jsem zvědav na připomínky od vás.

Upraveno 26. 4. 2007

Jirka Pech mě upozornil, že mi z návrhu vypadly „drobnosti“ jako cena jednotlivého úkolu a sazba DPH. V komentáři vysvětluji, jak k tomu došlo, nicméně velký dík za upozornění. Aktualizoval jsem obrázky i soubory ke stažení.

Upraveno 29. 4. 2007

Nemohl jsem jinak (i když jsem se bráníl ;) ), než dát za pravdu Jakubu Vránovi a uznat, že vztahová tabulka mezi fakturou a subjekty je zbytečná. Zrušil jsem ji a supplier_id i customer_id jsou nyní součástí tabulky invoice

Na návrh fousa (jestli komolím, tak se omlouvám), jsem oddělil adresu do tabulky address a přidal ještě address_kind pro jednotlivé druhy adres.

Navíc jsem vyhodil informace na kontaktní osobu a dal jsem je do zvláštní tabulky. Vedlo mě k tomu hlavně to, že v dalším díle se chci zabývat uživatelským rozhraní a zjednodušení nutných tabulek mi umožní, soustředit se opravdu jen na to důležité: položky faktury, subjekty a jejich náležitosti potřebné pro tisk faktury a pro základní evidenci faktur.

Aktualizoval jsem opět jak obrázky tady, tak zdrojové soubory, které jsou ke stažení.

Obrázek č. 4 – Nová databáze pro jednoduchou aplikaci na tisk a evidenci faktur


  1. Pro návrh databází používáme DBDesigner, protože jsme nenašli nic, co by bylo stejně dobré a fungovalo na Linuxu a Macu (+virtualizace Windows přes Parallels). Pokud někdo znáte program, který výše uvedené splňuje, dejte mi vědět.

    zpět na místo v textu

19