Tehtänä oli rakentaa luokkakuvaukset kaikista niistä tiedoista joita valokuvausliike toiminnassaan tarvitsee. Ei niinkään simppeli muttei mahdotonkaan.
Itse lähdin ratkaisemaan tehtävää määrittelemällä kriittisimmän päivän mitä voi eteentulla. Kuviteltuna päivänä pitäisi:
- Valmistaa valokuvia
- Myydä pari kameraa, toinen käteisasiakkaalle ja toinen yritykselle laskulla
- Käydä ottamassa hääkuvia
- Tilata täydennystä varastoon, kiire joten tämä tehtiin sähköpostilla
- karhuta yhtä laskua vuoden vanhasta hääkuvauksesta
- Tehdä inventaario
- Apunasi ollut myyjä irtisanoutui päivän päätteeksi
Kuvitteellinen tilanne mutta tuona päivänä tietomäärän käsittely on yllättävän korkealla. Koska liike itsessään on pieni, kirjanpidon hoitaa tilitoimisto. Tilitoimistoon hoidetaan perinteiseen tapaan paperilla kaikki. Nuuka yrittäjä tulostaa pankista itse tiliotteet ja toimittaa ne kirjanpitäjälle kunhan on tarkistanut saadut suoritukset.
Ensiksi mietin mitä kaikkea tietoa tarvitsee säilyttää ja hallita. Ei muuta kuin Visual Studiolla class diagrammia vääntämään. Suluissa on viittaus kuviteltuna päivänä tehtävään työhön
- kaiken takana yksi luokka joka huolehtii tietokantataulusta (dbtaulu). Seuraavan luokat siis perii tämän dbtaulun.
- tuote (1,2,3,6)
- asiakas (1,2,3,5)
- toimittaja (4)
- tilaus (1,2)
- tilausrivi (1,2)
- lasku (2,5)
- laskurivi (2,5)
- myyntireskontra (5)
- myyntireskontrarivi (5)
- maksuehto (5)
- myyjä (7)
- varasto (4)
- maksusuoritukset (5)
Tilaus, lasku, myyntireskontra periaatteessa voisi olla sama taulu. Ajattelin kuintenkin että varmistuksessa myyntireskontralla on suurempi painoarvo kuin tilauksilla. Lisäksi, tietomäärää kasvaa tilauksesta, laskuun ja lopuksi reskontraan.
Koska yksinkertaisimpia tauluja ja samalla luokkia on maksuehto ja myyjä lähdin ratkaisemaan niitä ensiksi. Ja eiku koodaamaan. Luokkadiagrammi syntyi pähkäillen näitä tauluja ja näyttipä se hienolta. Oli periytetty kantaluokasta seuraava, edellinen, lisää, poista, muokkaa yms ”perus” jutut mukaan. Oli jopa interface määritelty jolla huoldehtitaan että jokainen luokka sisältää ne vaadittavat tekijät.
Tässä mallikuva maksuehtoluokasta:
No, tästä pääsi sitten kommentoimaan ja koodaamaan toiminnallisuutta. Ja, sainhan sen toimimaankin. Tosin, koodin siinä osassa joka huolehtii tiedon näyttämisestä ja vastaavista toimenpiteistä tuli yllättävän hankala ja työläs ylläpitää. Mutta tulisihan se nippa nappa täyttämään sen mitä vaadittiinkin (kaikki tieto vain oli tietokannassa suoraan). Aikaakaan ei mennyt kuin 8 iltaa.
Entäs jos lähtisi siitä että luokka sisältää listan tiedoista ja vieläpä siten että luokan voisi heittää suoraan vaikka datagridview:ille?
Kantaluokista (joista periytetään muut) tuli rautalankana tällainen:
[sourcecode lang=”csharp”]
// Ensin luokka joka on tasoa atom, (yksittäinen maksuehto yms)
public abstract class BusinessBase
{
/// <summary>
/// Business Base: constructori
/// </summary>
public BusinessBase()
{
}
/// <summary>
/// Update komennon pohja
/// </summary>
public virtual void Update()
{
}
/// <summary>
/// Delete komennon pohja
/// </summary>
public virtual void Delete()
{
}
/// <summary>
/// Update komennon pohja
/// </summary>
public virtual void Update()
{
}
/// <summary>
/// Yhden rivin lukutoiminnon pohja
/// </summary>
protected virtual void Read()
{
}
}
// Sitten luokka joka sisältää atomit
public class BusinessListBase<T> : BindingList<T> where T : BusinessBase
{
}
[/sourcecode]
Nyt oli siis perusluokka määritelty. Mika Kolari on kirjoittanut yhteenvedon eri geneerisistä kokoelmista. Kannattaa tulostaa talteen.
Ja sitten tietokantataulun mukainen luokka:
[sourcecode lang=”csharp”]
public class myyja : BusinessBase
{
// luokalle toimintaa, kentät yms
private int _myyjaID=0;
private string _myyjanimi="";
private void Read(SqlDataReader dr)
{
this._myyjaID = Convert.ToInt16(RuntimeHelpers.GetObjectValue(dr["myyjaID"]));
this._myyjanimi = Convert.ToString(RuntimeHelpers.GetObjectValue(dr["myyjaNimi"]));
}
public override void Update()
{
//tähän tulee yksittäisen myyjän update toiminnot, poisto, lisäys ja muutos (delete,insert,update). Helpoiten menisi stored procedurella?
}
public int myyjaID
{
get { return this._myyjaID; }
set { this._myyjaID = value; }
}
public string myyjaNimi
{
get { return this._myyjanimi; }
set { this._myyjanimi = value; }
}
}
public class myyja_list:BusinessListBase<myyja>
{
/// <summary>
/// Luetaan myyjät sisään
/// </summary>
public void Read()
{
}
/// <summary>
/// Luodaan myyjä luettelo ja haetaan kannasta tiedot
/// </summary>
/// <returns></returns>
public static myyja_list Getmyyja_list()
{
myyja_list list2 = new myyja_list();
list2.Read();
return list2;
}
public static myyja_list Getmyyja_list_dummy()
{
myyja_list list2 = new myyja_list();
//list2.Items.Add(myyja.GetDummy());
//Jos halutaan pelata ilman tietokanta, luodaan myyja luokalle vielä oma GetDummy funktio joka palauttaa myyjä luokan satunnaisella sisällöllä
return list2;
}
}
[/sourcecode]
Jos BusinessListBaseen olisi kytkettynä IBindingListView interface, saataisiin sorttaukset ja filtteröinti toteutettua.
Huom. koodit ei itsessään toimi vaan tarkoitus on näyttää rautalankamalli jonka päälle toteutin toiminnallisuuden. Varsinaisessa formissa jossa tietoa käsitellään luonti ja kytkentä tapahtuu seuraavasti:
[sourcecode lang=”csharp”]
private void SimpleGrid_Load(object sender, EventArgs e)
{
//Luodaan bindingsourceen myyja_list luokka tietoineen
this.bindingSource1.DataSource = myyja_list.Getmyyja_list();
//Sallitaan uusien rivien lisäys
this.bindingSource1.AllowNew = true;
}
public SimpleGrid()
{
//luodaan eventti joka laukeaa kunhan formin omat kuviot on selvät
base.Load += new EventHandler(this.SimpleGrid_Load);
InitializeComponent();
}
[/sourcecode]
Formilla oli siis valmiiksi bindingSource1 joka on kytketty datagridview komponenttiin. Tässä vain ilmoitetaan bindingSource:lle tietolähde josta tiedot haetaan.