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:
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
-
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.
Jan Korbel: Příklad návrhu web aplikace…
Jan Korbel začal s vydáváním seriálu článků o návrhu webových aplikací.
…
Vyborny clanok. Prinutil ma zamysliet sa nad vlastnym postupom navrhu. A som vdacny za odkaz na DBDesigner, doteraz som tento software nepoznal, ale vyzera uzitocne. Dufam, ze k clanku coskoro pribudnu pokracovania a autorovi prajem vela chuti do ich pisania.
FrozenDog: Díky, díky… Já si nikdy nejsem jistý, jestli nepublikuju něco, co všichni znají, takže jakákoli zpětná vazba mi hodně pomáhá. Když je ještě takhle kladná, tak je to radost. Potřebuju vědět, že si tu jen tak nemluvím do zdi.
Ad DBDesigner: Jo, je to slušná aplikace, škoda, že už ji nevyvíjí dál. Ale jak tam píšu, zatím jsme nic lepšího za ty peníze ;) nenašli.
Ahoj Honzo,
je to pěkné, ale nikde tam nevidím položkové ceny (čekal bych je jako součást tasku) nebo alespoň cenu celkem a daňový zápis (které by mohly být součástí invoice). Minimálně cena celkem a vyúčtování DPH jsou, řekl bych, povinnou součástí faktur, ne?
Je mi, musím přiznat, tak trochu záhadou, jak to vaše fakturování bez cen vlastně funguje. To je máte snad skryté v nějakém z textů? Jak potom rozdělíte kořist? ;-)
Díky.
Jirka Pech: Ahoj, jsem rád, že tu někdo přemýšlí nad tím, co vidí. Tohle mi opravdu uteklo, ale už vím, jak se to stalo.
Já jsem měl totiž v hlavě ještě navázání úkolu na evidenci času, který jsme nad ním strávili, ale pak jsem se rozhodl ji vynechat, protože by tenhle příklad komplikovala a bylo by pak zbytečně složité řešit webové rozhraní. Nechával jsem si to jako zálohu pro případ, že by byl prostor v dalších dílech.
To nic nemění na tom, že neuvést prostor pro cenu a sazbu DPH u jednotlivého úkolu byla chyba a přivádí mě to k něčemu, co jsem v textu nezmínil a to, jak je důležité svůj návrh konzultovat s někým, kdo na něm nedělal s vámi, s někým, kdo bude mít čerstvý pohled. Někdy se do toho člověk tak zaplete, že mu pak utečou takovéhle zásadní záležitosti. Ale návrh webového rozhraní by nás na tuhle botu upozornil velmi rychle.
Je nějaký důvod pro existenci tabulky
invoice_has_subjects? Sloupcesupplier_idacustomer_idby přeci mohly být přímo v tabulceinvoice.Podle mne je to kvuli tomu, že je někdy potřeba aby faktura měla jiný počet než PRÁVĚ jednoho zákazníka a PRÁVĚ jednoho prcovnika :D I když realný význam si moc představit neumím. Že by se vystavovala jedna faktura pro tři firmy najednou? :D
Jakub Vrána: Hmmm, dobrá připomínka. Po pravdě nevím, co mě k tomu vedlo. Vrtá mi hlavou, jestli jsem nechtěl umožnit navázání nějakých vlastností na ten vztah… Ještě si to nechám rozležet v hlavě, ale spíš to bude, jak říkáte a změnu budu reflektovat úpravou modelu a obrázků asi ale až zítra, protože dnes nebude čas.
Díky za připomínku.
Tomáš Fejfar: Noooo, dobrá inspirace. Zkusím z toho ještě nějak vybruslit :D
Opravdu velice hezký článek, zvláště užitečný mi příjde ten DBDesigner
Tiež som pred pár týždňami narazil na DBDesigner, celkom dobrý program, teda až na nepoužiteľnosť prefixov tabuliek…
No myslím, že budete brzy překopávat i tabulku subject neb už jsem narazil na to, že adres k jednomu subjektu může být vícero (sídlo, faktuační adresa, kam to poslat…)
DBDesigner… dobrý, ještě můžete zkusit Toad Data Modeler http://www.toadsoft.com/toaddm/toad_data_modeler.htm ale nic lepšího jsem taky nenašel ;)
lix: Jo, na Toad Data Modeler jsem narazil, ale je jen na Wokna (potřebujeme i Linux a ideálně OS X) a pak cena $479 mě poněkud odrazuje. Navíc ten jejich web je odpornej ;)
Pročpak ty názvy tabulek nejsou česky? Myslím, že by to mnohé upřesnilo. Taky nevím, jestli jsou ty názvy dostatečně popisné. Nedávno jsem při návrhu databáze asi půl hodiny přemýšlel a procházel slovník, abych našel to správné slovo, které tabulku maximálně popisuje (ano, bylo to slovo způsobilost) :)
Taky by myslím neškodilo zobrazovat kardinalitu (píšu-li to správně – mám na mysli vzhahy 1:1, 1:N, M:N…), pravděpodobně by se hned odhalilo to, co zde zmínil Jakub Vrána.
No, my tu máme takovou dohodu, že se všechno pojmenovává anglicky. Čestina má ty svoje háčky a čárky a ono to dělá neplechu. Součástí modelu databáze jsou i komentáře k jednotlivým tabulkám, které vše vysvětlují, ale ty už se mi sem nevešly.
DBDesigner to řeší jen těma šipkama s černým zobáčkem… Nic moc, ale co se dá dělat ;)
Jak se budou řešit změny subjektu v čase?
Například když se firma přejmenuje nebo přestěhuje, změní se při současném modelu údaje na již vystavených fakturách. Asi by bylo vhodné sledovat jaké údaje byly na faktuře v den jejího vystavení nebo sledovat pro jaké období jsou údaje v tabulce address platné.
Jiří Kubíček: Jojo, už mě na to někdo upozorňoval. Počítám s tím, že se bude ještě pár věcí měnit, jak budu tenhle teoretický návrh implementovat do reálného rozhraní. Proto jsem si to taky nechal na Railsy :) Jsem velkým příznivcem filozofie 37signals – Iterate, iterate, iterate! Tahle série článků byla jen takový pohled na moje uvažování, rozhodně ne hotový produkt.
dobry den mam problem potřebovala bych najit program pro jedno duchý tisk faktur nap dodavatel odběratel vaha zviřete cena atd.Prostě pro malo zemědělce v živočišnej vyrobě.díky Lukasová nevím co a jak.