Avatar
hanse
Člen
Avatar
hanse:

Zdravím,

jedná se o mojí trochu smyslplnější aplikaci - kalkulačku daňových odpisů.
Rád bych se dozvěděl další názor, například hlavní nedostatky, případně jestli nedělám někde zásadní chyby.

Aplikace umí

  • vypočítat zrychlené a rovnoměrné odpisy dle zadaných údajů
  • exportovat do .txt a excelu

Do budoucna uvažuju například o:

  • přidání dalších parametrů
  • přidání seznamu majetku a ukládání/výpis odpisů podle něj (čímž dojde i k odpadnutí části proměnných)
  • přidání další nabídky pro rovnoměrné odpisování (např. související se zvýšenou vstupní cenou)
  • přidání tisku
  • vylepšení / dopracování uživatelského rozhraní, menu...

Link ke stažení:
http://uloz.to/…D/do2013-zip

zdrojový kód:

namespace Odpisovac
{
    public partial class Odpisovac : Form
    {

        /// <summary>
        /// Proměnná pro pořizovací cenu (zadanou uživatelem).
        /// </summary>
        private decimal cena;
        /// <summary>
        /// Proměnná pro zůstatkovou cenu.
        /// </summary>
        private decimal zcena;
        /// <summary>
        /// Proměnná pro počet let (dle odpisové třídy zadané uživatelem)
        /// </summary>
        private byte pocetLet;
        /// <summary>
        /// Proměnná pro rok pořízení majetku a začátku jeho odpisování (zadáná uživatelem).
        /// </summary>
        private int letopocet;
        /// <summary>
        /// Proměnná představující částku odpisu v daném roce.
        /// </summary>
        private decimal odpis;
        /// <summary>
        /// Promenná představující koeficient u rovnoměrných odpisů.
        /// </summary>
        private decimal koeficient;
        /// <summary>
        /// Řádek ve vystupListView.
        /// </summary>
        private ListViewItem item;
        /// <summary>
        /// Pole pro počty let odpisování definované zákonem dle odpisových skupin.
        /// </summary>
        byte[] poctyLet = { 3, 5, 10, 20, 30, 50 };
        /// <summary>
        /// Pole pro koeficienty rovnoměrného odpisování definované zákonem dle odpisových skupin.
        /// </summary>
        decimal[] koeficienty = { 40, 22.25m, 10.5m, 5.15m, 3.4m, 2.02m };
        /// <summary>
        /// Pole pro jednotlivé údaje na 1 řádku..
        /// Délka pole = počet sloupců.
        /// </summary>
        string[] zaznam = new string[4];
        /// <summary>
        /// Proměnná představující částku oprávek.
        /// </summary>
        private decimal opravky;
        /// <summary>
        /// Proměnná pro chybu zápisu do souboru.
        /// </summary>
        private bool chybaZapisu = false;
        /// <summary>
        /// Proměnná pro typ odpisů (zrychlené = true, rovnoměrné = false)
        /// </summary>
        private bool typ;
        /// <summary>
        /// List náplň pro výpis v excelu.
        /// </summary>
        List<string> napln = new List<string>();

        /// <summary>
        /// Zobrazí okno, nastaví úvodní hodnoty.
        /// </summary>
        public Odpisovac()
        {
            InitializeComponent();
            porizovaciCenaTextBox.Text = "0";
            DateTime datumCas = DateTime.Now;
            rokPorizeniTextBox.Text = datumCas.Year.ToString();
            odpisovaSkupinacomboBox.SelectedIndex = 0;
            vystupListView.FullRowSelect = true;
        }

        /// <summary>
        /// Metoda kontroluje vstupní údaje a případně spoustí výpočty.
        /// </summary>
        private void vypoctiButton_Click(object sender, EventArgs e)
        {
            //pomocná proměnná (true při zjištěné chybě)
            bool chyba = false;
            typ = zrychleneRadioButton.Checked;
            //kontrola vstupů
            if (!decimal.TryParse(porizovaciCenaTextBox.Text, out cena) | (cena < 2000))
            {
                MessageBox.Show("Neplatná hodnota - Pořizovací cena! Pořizovací cena musí být vyšší než 2 000!", "Zpráva", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                chyba = true;
            }
            if (!int.TryParse(rokPorizeniTextBox.Text, out letopocet) | (letopocet < 1900) | (letopocet > 2100))
            {
                MessageBox.Show("Neplatná hodnota - Rok pořízení! Zadejte hodnotu v rozmezí 1900 - 2100.", "Zpráva", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                chyba = true;
            }
            //spuštění nastavení a výpočtů
            if (!chyba)
            {
                cena = Math.Ceiling(cena);
                zcena = cena;
                vystupListView.Items.Clear();
                int p = odpisovaSkupinacomboBox.SelectedIndex;
                pocetLet = poctyLet[p];
                koeficient = koeficienty[p];
                if (typ)
                    ZrychleneOdpis();
                else
                    RovnomerneOdpis();
            }
        }

        /// <summary>
        /// Metoda pro výpočt zrychlených odpisů.
        /// Volá metodu vypisRadek() pro přídání do vystupListView.
        /// </summary>
        private void ZrychleneOdpis()
        {
            odpis = Math.Ceiling(zcena / pocetLet);
            zcena = zcena - odpis;
            VypisRadek();
            letopocet = letopocet + 1;
            for (int i = 1; i < pocetLet; i++)
            {
                odpis = Math.Ceiling(2 * zcena / (pocetLet + 1 - i));
                zcena = zcena - odpis;
                VypisRadek();
                letopocet = letopocet + 1;
            }
        }

        /// <summary>
        /// Metoda pro výpočet rovnoměrných odpisů.
        /// Volá metodu vypisRadek() pro přídání do vystupListView.
        /// </summary>
        private void RovnomerneOdpis()
        {
            odpis = cena * (100 - (koeficient * (pocetLet - 1))) / 100;
            odpis = Math.Ceiling(odpis);
            zcena = zcena - odpis;
            VypisRadek();
            letopocet = letopocet + 1;
            for (int i = 1; i < pocetLet; i++)
            {
                if (i == pocetLet -1 & zcena < odpis)
                {
                    odpis = zcena;
                    zcena = zcena - odpis;
                    VypisRadek();
                }
                else
                {
                    odpis = cena * koeficient / 100;
                    odpis = Math.Ceiling(odpis);
                    zcena = zcena - odpis;
                    VypisRadek();
                    letopocet = letopocet + 1;
                }
            }
        }


        /// <summary>
        /// Metoda pro vypsání 1 naformátovaného řádku tabulky (vystupListView).
        /// </summary>
        private void VypisRadek()
        {
            opravky = cena - zcena;
            zaznam[0] = letopocet.ToString();
            zaznam[1] = odpis.ToString() + String.Format("{0:c}", " Kč");
            zaznam[2] = zcena.ToString() + String.Format("{0:c}", " Kč");
            zaznam[3] = opravky.ToString() + String.Format("{0:c}", " Kč");
            item = new ListViewItem(zaznam);
            vystupListView.Items.Add(item);
        }

        /// <summary>
        /// Metoda kontroluje, že má co exportovat, a když ano volá metodu Exportuj()
        /// </summary>
        private void exportButton_Click(object sender, EventArgs e)
        {
            if (vystupListView.Items.Count == 0)
                MessageBox.Show("Chybí vstupní data pro vytvoření souboru.", "Zpráva", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
            else
                Exportuj();
        }

        /// <summary>
        /// Metoda pro export údajů z vystupListView do externí souboru (dle údajů z vyvolaného saveFileDialogu)
        /// </summary>
        private void Exportuj()
        {
            napln.Clear();
            int i = 0;;
            string radek;
            saveFileDialog.Filter = "Textové dokumenty (*.txt)|*.txt|Sešit aplikace Excel(*.xlsx)|*.xlsx|Všechny soubory (*.*)|*.*";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                foreach (ListViewItem item in vystupListView.Items)
                {
                    if (chybaZapisu)
                        break;
                    item.Selected = true;
                    //nastaveni vystupu
                    radek = "";
                    for (int j = 0; j < zaznam.Length; j++)
                    {

                        zaznam[j] = vystupListView.SelectedItems[i].SubItems[j].Text;
                        radek += zaznam[j].PadRight(20);
                    }

                    if (saveFileDialog.FileName.ToString().Contains("txt"))
                        zapisTXT(radek, i);
                    else if (saveFileDialog.FileName.ToString().Contains("xlsx"))
                        naplnExcel(i);
                    i++;
                }
            }
        }

        /// <summary>
        /// Metoda pro samotný zápis do souboru
        /// </summary>
        /// <param name="radek">Text řádku výstupu</param>
        /// <param name="i">Pořadí řádku - pro vypsání hlavičky.</param>
        private void zapisTXT(string radek, int i)
        {
            try
            {
                if (i == 0)
                {
                    using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName))
                    {
                        if (typ)
                            sw.WriteLine("Zrychlené odpisy");
                        else
                            sw.WriteLine("Rovnoměrné odpisy");

                        sw.WriteLine();
                        sw.WriteLine("Cena pořízení: " + cena.ToString());
                        sw.WriteLine();
                        sw.WriteLine("Rok".PadRight(20) + "Odpis".PadRight(20) + "Zůstatková cena".PadRight(20) + "Oprávky");
                        sw.WriteLine(radek);
                        sw.Flush();
                    }
                }
                else
                {
                    using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName, true))
                    {
                        sw.WriteLine(radek);
                        sw.Flush();
                    }
                }
            }
            catch
            {
                MessageBox.Show("Chyba zápisu do souboru.", "Zpráva", MessageBoxButtons.OK, MessageBoxIcon.Error);
                chybaZapisu = true;
            }
        }


        /// <summary>
        /// Metoda pro naplnění Listu náplň.
        /// Při posledním volání zavolá metodu zapisExcel().
        /// </summary>
        /// <param name="i">prom. počítadlo</param>
        private void naplnExcel(int i)
        {
            for (int j = 0; j < zaznam.Length; j++)
                napln.Add(zaznam[j]);
            if (i == pocetLet - 1)
            {
                zapisExcel();
            }
        }

        /// <summary>
        /// Metoda pro zapsání výstupu do Excelu.
        /// Využívá externí knihovny (todo: přijít na způsob inteligentního/kratšího formátování)
        /// </summary>
        private void zapisExcel()
        {
            FileInfo novySoubor = new FileInfo(saveFileDialog.FileName.ToString());
            try
            {
                if (novySoubor.Exists)
                {
                    novySoubor.Delete();
                    novySoubor = new FileInfo(saveFileDialog.FileName.ToString());
                }
                using (ExcelPackage package = new ExcelPackage(novySoubor))
                {
                    // Vytvoření nového listu
                    ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Odpisy");

                    int m = 0;
                    decimal bunka;

                    //hlavička
                    if (typ)
                        worksheet.Cells[1, 1].Value = "Zrychlené odpisy";
                    else
                        worksheet.Cells[1, 1].Value = "Rovnoměrné odpisy";
                    worksheet.Cells[1, 1].Style.Font.Size += 6;
                    worksheet.Cells[1, 1].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
                    worksheet.Cells[1, 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                    worksheet.Cells[1, 1, 1, 4].Merge = true;
                    worksheet.Cells[3, 1].Value = "Pořizovací cena:";
                    worksheet.Cells[3, 3].Value = cena;
                    worksheet.Cells[3, 3].Style.Numberformat.Format = "#,##0 Kč";
                    worksheet.Cells[3, 1, 3, 2].Merge = true;
                    worksheet.Cells[5, 1].Value = "Rok";
                    worksheet.Cells[5, 2].Value = "Odpis";
                    worksheet.Cells[5, 3].Value = "Zůstková cena";
                    worksheet.Cells[5, 4].Value = "Oprávky";
                    worksheet.Column(1).Width = 8;
                    for (int i = 2; i < zaznam.Length + 1; i++)
                        worksheet.Column(i).Width = 17;

                    worksheet.Cells[1, 1, 5, 4].Style.Font.Bold = true;

                    for (int r = 6; r < pocetLet + 6; r++)
                    {
                        for (int s = 1; s < zaznam.Length + 1; s++)
                        {
                            if (decimal.TryParse(napln[m].Replace(" Kč", ""), out bunka))
                            {
                                worksheet.Cells[r, s].Value = bunka;
                            }
                            else
                                worksheet.Cells[r, s].Value = zaznam[m];
                            m++;
                            if ((r == pocetLet + 5) & (s == zaznam.Length))
                            {
                                worksheet.Cells[5, 1, r, s].Style.Border.Top.Style = ExcelBorderStyle.Thin;
                                worksheet.Cells[5, 1, r, s].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
                                worksheet.Cells[5, 1, r, s].Style.Border.Left.Style = ExcelBorderStyle.Thin;
                                worksheet.Cells[5, 1, r, s].Style.Border.Right.Style = ExcelBorderStyle.Thin;
                                worksheet.Cells[5, 1, r, s].Style.Border.BorderAround(ExcelBorderStyle.Medium);
                                worksheet.Cells[6, 2, r, s].Style.Numberformat.Format = "#,##0 Kč";
                                worksheet.Cells[5, 1, r, 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                                worksheet.Cells[5, 2, 5, s].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                            }
                        }
                    }
                    package.Save();
                }
            }
            catch
            {
                MessageBox.Show("Chyba zápisu do souboru.", "Zpráva", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


    }
}
Editováno 18.5.2013 14:51
 
Odpovědět 18.5.2013 14:50
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na hanse
Drahomír Hanák:

Pěkné, ale mohl bys to rozdělit do víc tříd. Takhle je to strašně přeplácané a nepřehledné.

 
Nahoru Odpovědět 18.5.2013 14:55
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na hanse
David Čápka:

Docela hezká třída, máš tam pár metod s malým písmenem :P Co je ExcelWorksheet, něco 3. strany nebo od MS?

Nahoru Odpovědět 18.5.2013 15:01
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
hanse
Člen
Avatar
hanse:

Díky za názory:)

2Drahoš: ok - v tomhle případě mi přijde nejlepší dát do vlastní třídy metody týkající se exportu (do budoucna, v závislosti na realizaci ostatních plánů asi i samotné kalkulace a třídu pak nechat jen čistě pro zadávání/zobra­zování)

2sdraco: ty názvy metod sem nějak přehlídnul ;( ExcelWorksheet je 3. strany

 
Nahoru Odpovědět 18.5.2013 15:29
Avatar
hanse
Člen
Avatar
hanse:

Pracuji na návrhu další verze a řešim problém výhodnosti načítání dat z databáze / jejich dopočítání (počítám s tím, že se na začátku načte seznam majetku* z databáze na úvodní obrazovku s jeho seznamem, a pak bude možné rozkliknout na přehled/přidá­ní/úpravu odpisů daného majetku):

  1. možnost: veškěrá (případně skoro veškerá) data včetně těch o odpisech by se načítala z databáze
  2. možnost: data o odpisech (do přehledu odpisů k danému majetku) by se pokaždé dopočítala (víceméně dle původního návrhu, viz první post)

Uvažuji o rozdílu běhu rychlosti aplikace v případě několika položek majetku (kde rozdíl bude zanedbatelný) vs několik tisíc kusů majetku (a tím pádem např. desítky tisíc řádků odpisů vyhledávaných dle ID majetku). Rád bych si oveřil/vyvrátil svoji hypotézu, a proto bych poprosil, jestli by se někdo zkušenější nemohl vyjádřit.

*pozn.: počítám s přednastaveným omezením na xy položek majetku + filtry/vyhledávání, aby úvodní načítání při většího množství položek majetku v databázi netrvalo příliš dlouho

Editováno 23.5.2013 10:38
 
Nahoru Odpovědět 23.5.2013 10:37
Avatar
hanse
Člen
Avatar
hanse:

tak nic, vyřešeno + nebylo by možné alespoň do inkubátoru přidat možnost editace?

 
Nahoru Odpovědět 24.5.2013 22:29
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na hanse
David Čápka:

Inkubátor je jen pro oznámení, pokud chceš něco prezentovat vážněji, přidej to jako program :)

Nahoru Odpovědět 25.5.2013 9:29
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
hanse
Člen
Avatar
 
Nahoru Odpovědět 25.5.2013 9:36
Avatar
Kit
Redaktor
Avatar
Odpovídá na hanse
Kit:

Pokud chceš editovat soubor i po vložení, používej "Vložení dlouhého zdrojáku" pod okénkem příspěvku.

Nahoru Odpovědět 25.5.2013 10:42
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
hanse
Člen
Avatar
hanse:

Díky za tip, vyzkouším u dalšího projektu

Editováno 25.5.2013 12:28
 
Nahoru Odpovědět 25.5.2013 12:27
Avatar
hanse
Člen
Avatar
hanse:

Zdravím,

tak po nějaké době sem dávám další trochu funkčnější verzi Daňových odpisů:

ke stažení:
http://uloz.to/…o2013v01-zip
podstatné části zdrojového kódu:
http://www.itnetwork.cz/dev-lighter/108

Hlavní změny:

  • dočasně odebrána možnost exportu
  • přidána správa majetku - Přehled majetku, Přidání majetku (databáze majetku a odpisů) a další související/pomocné třídy (více vrstev)
  • překopána třída Odpisovač

poznámky k ovládání:

  • do odpisovače (pro přehled a kalkulaci odpisů určité položky majetku) se dostanete rozkliknutím položky majetku v přehledu majetku.
  • pro řazení položek majetku klik na záhlaví sloupce.

V plánu:

  • možnost úprav a mazání vybraných a všech položek majetku (a zároveň příslušných odpisů)
  • Export/Import do/z Excelu (excelovský dokument by sloužil jako záloha + možnost přenositelnosti mezi PC)
  • přidání dalších možností pro odpisování
  • menu, zapracovat na vzhledu
  • další drobné změny, úpravy a vylepšení kódu

Dotazy:

  • Ve třídě Filtrovac metoda Zobraz - zatím jsem nepřišlel za pomocí Linq to SQL na lepší řešení - nevěděl by prosim někdo, jak to dát jednoho dotazu, respektive jestli je to možné (ideálně aby nedošlo zároveň ke zpomalení běhu dotazu)?
  • Uživatelské rozhraní - sice jsem už zvažoval několik možností, ale zatím nemám konkrétní představu, takže bych poprosil o vaše návrhy.
  • Nápady, názory, možnosti vylepšení, co vás napadne.
 
Nahoru Odpovědět 8.6.2013 17:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 11 zpráv z 11.