Projekt:
Program zarządzający gospodarką magazynową.
Dokumentacja Techniczna.
Krzysztof Kula
Lab 3
SPIS TREŚCI
Wstęp
Program jest pracą zaliczeniową laboratoriów „Projekt”. Baza3 jest już 3 wersją programu, wynika to ze zmiany środowiska programistycznego w czasie tworzenia programu, oraz w późniejszym czasie z całkowitej reorganizacji projektu programu w dość zaawansowanym stadium jego tworzenia.
Za język programowania został użyty C++ gdyż autor ma największe doświadczenie w pracy z nim, a jednocześnie umożliwia pisanie nawet dużo bardziej złożonych programów z wykorzystaniem klas, co wymiernie ułatwia sam proces pisania.
WxDev-C++, wraz z kompilatorem GCC zostały użyte do stworzenia graficznego interfejsu użytkownika, wykorzystywałem bibliotekę wxWidgets, domyślną dla tego środowiska. WxDev-C++ jest darmowym programem, którego nie trzeba nawet rejestrować. Choć nie jest pozbawiony błędów i to czasem krytycznych, to jednak po przyzwyczajeniu się do interfejsu pisanie nie sprawia żadnych problemów.
Program miał za zadanie komunikować się z lokalną bazą danych, do tego celu wybrałem bazę danych Firebird, a do łączenia się z nim wykorzystana została biblioteka IBPP. Jest ona darmowa oraz posiada otwarty kod źródłowy i dość dobrą dokumentację. Oczywiście największym atutem jest to, że dostępna jest w formie, DevPaka tzn. zbioru bibliotek do **Dev-C++, przez co bardzo proste jest jej doinstalowanie do zainstalowanego na komputerze wxDev-C++.
Implementacja poszczególnych okien programu umieszczona została w oddzielnych plikach *.h *.cpp przez co znajdowanie poszczególnych części programu jest dużo prostsze niż gdy cały kod znajduje się w jednym pliku.
Przy starcie programu, (jeśli jest dostępny) widoczny jest „splash screen”, a więc obrazek startowy, na którym widoczne są emblematy wszystkich bibliotek i programów użytych podczas tworzenia programu.
Dodatkowo wxDev-C++ pozwala na stworzenie dokumentacji w formie strony HTML, nie jest to pozbawione błędów (widoczne „krzaczki” przy formie aplikacji), jednak pozwala przeglądać kod w dużo „lżejszej” przeglądarce (nie trzeba otwierać projektu przy pomocy wxDev-C++) a jednocześnie pozwala na łatwe poruszanie się w strukturze plików projektu.
Diagramy UML.
Diagramy, które umieszczone są w dokumentacji zostały stworzone z pomocą programu „StarUML” przy użyciu „Reverse Engineer…”. Funkcja ta pozwoliła mi w dość szybki sposób stworzyć te diagramy klas, ukazujące także strukturę klas (zawieranie). Opisy połączeń asocjacyjnych odpowiadają nawom pól w metodach przechowujących obiekty danej podklasy. Nie są widoczne parametry funkcji dla zwiększenia czytelności diagramów. Zarówno diagramy jak i ich obrazy w plikach *.JPG są umieszczone na płycie.
Diagram klas.
Diagram klasy Baza3FrmApp.
Diagram klasy okna głównego Baza3Frm.
Diagram klasy okna magazynu MagazynDlg.
Diagram klasy okna faktury FakturaDlg.
Diagram klasy okna stanów magazynowych StanyMagazynoweDlg.
Połączenie z Firebird - biblioteka IBPP.
Biblioteka, aby móc z niej korzystać wymaga od programisty pewnych zmian w programie. Konieczne było włączenie obsługi wyjątków w kompilatorze GCC, dopisanie „- libpp” do opcji konsolidatora oraz dodanie wpisu: „C:\Dev-Cpp\include\ibpp” do katalogów plików nagłówkowych. Dopiero po tym możliwe było dołączenie funkcjonalności biblioteki do tworzonego programu przez:
#ifndef __IBPP_h__
#include <ibpp.h>
#define __IBPP_h__
#endif
Jednakże zanim możemy użyć #define __IBPP_h__ konieczne jest zdefiniowanie flag określających system operacyjny i rodzaj kompilatora:
//IBPP
#ifndef IBPP_GCC
#define IBPP_GCC
#endif
#ifndef IBPP_WINDOWS
#define IBPP_WINDOWS
#endif
Używane zmienne, klasy i ich krótka charakterystyka.
Krótko opiszę wszystkie zmienne wykorzystywane przez bibliotekę w kolejności ich deklaracji w pliku Baza3Frm.h:
const char* DbName; // FDB extension (GDB is hacked by Windows Me/XP "System Restore")
DbName jak sama nazwa wskazuje przechowuje nazwę do bazy danych, mam tu na myśli ścieżkę do pliku wraz z nazwą pliku znajdującego się na dysku twardym komputera.
std::string ServerName;// = "localhost";
ServerName przechowuje nazwę serwera z uruchomioną bazą Firebird, w programie domyślnie jest wykorzystywany localhost a więc komputer, na którym został uruchomiony program.
std::string UserName;// = "SYSDBA";
UserName przechowuje nazwę użytkownika bazy danych.
std::string Password;// = "masterkey";
Password zwiera hasło użytkownika SYSDBA.
Na koniec klasy samej biblioteki:
IBPP::Database db1;//WSKAZNIK DO BAZY DANYCH
Przechowuje wskaźnik do bazy danych, z którą program się połączył.
IBPP::Transaction tr1; //wskaźnik do transakcji
Jest konieczne to stworzenia klasy st1, jej możliwości omówię w dalszej części dokumentacji.
IBPP::Statement st1;
Klasa ta jest chyba najczęściej wykorzystywana, umożliwia, bowiem tworzenie zapytań do bazy danych.
Używane w programie metody klas wraz z opisem działania.
Biblioteka IBPP oprócz klas zawiera także funkcje, które np. tworzą połączenie z bazą danych - będzie to pierwsza funkcja, jaką opiszę:
db1 = IBPP::DatabaseFactory(ServerName, DbName, UserName, Password,"", "WIN1252", "PAGE_SIZE 8192 DEFAULT CHARACTER SET WIN1252");
Jak widać w funkcji tej wykorzystywane są wcześniej zadeklarowane zmienne, ale także inne określające typ połączenia. Funkcja ta tworzy połączenie, ale nie otwiera go.
db1->Create(3); // 3 is the dialect of the database (could have been 1)
Posiadając już połączenie z bazą danych możemy(o ile plik bazy danych nie istnieje) stworzyć go w systemie plików. Liczba 3 oznacza dialekt bazy danych.
db1->Connect();
Dopiero ta metoda klasy Database otwiera połączenie z bazą danych.
db1->Disconnect();
Metoda ta jest przeciwieństwem Connect, przerywa bowiem połączeni z bazą danych.
Jeśli baza została prawidłowo otwarta można zainicjować zmienne tr1 i st1.
tr1 = IBPP::TransactionFactory(db1,IBPP::amWrite,IBPP::ilConcurrency,IBPP::lrWait);
Zostaje przypisany do tr1 nowy obiekt transakcji, transakcja umożliwia zaakceptowanie zmian dokonanych w bazie danych, ale także cofnięcie wszelkich zmian do zapisanego punktu, jest także konieczna do zainicjowani st1.
tr1->Start();//start transakcji
Prosta metoda Start() uruchamia transakcję.
tr1->Commit();
Metoda zapisuje wszelkie zmiany I zamyka transakcję.
tr1-> CommitRetain();
Metoda zapisuje zmiany, ale nie zamyka transakcji.
tr1-> Rollback();
Cofa wszystkie zmiany jakie zaszły od ostatniego zapisu oraz zamyka transakcję.
st1 = IBPP::StatementFactory (db1, tr1);//nowy statement
StatementFactory jest ostatnią funkcją biblioteki, którą używam, dzięki obiektowi klasy Statement możliwe jest wysyłanie zapytań do bazy danych. Konieczne jest jednak używanie zmiennych tekstowych std::string zamiast wxString;
st1->Close();
Służy do zamknięcia obiektu st1.
st1->Prepare(polecenie);
st1->Execute();
Jest to para poleceń, których zwykle używałem kolejno. Pierwsze przygotowuje polecenie, drugie je wykonuje. Zmienna „polecenie” powinna być typu std::string.
while(st1->Fetch())
{
std::string nmag;
st1->Get(1, nmag);
magazyny.Add(nmag);
}
Powyższy fragment kodu zawiera kilka ważnych metod: Fetch() (jeśli zapytanie zwróciło jakieś wyniki) pobiera rekord wyników. Metoda Get(1, nmag); pozwala na zapisanie zwróconej zmiennej w programie, jest ona przeciążona i obsługuje wszystkie główne typy danych. Ostatnia linijka to zapis wczytanej danej.
Bardzo ważną rolę pełnią wyjątki i ich obsługa w bibliotece. Praktycznie każda metoda przeze mnie omówiona może zwrócić wyjątek typu IBPP::Exception& e:
try{
if(!tr1->Started()) tr1->Start();
}catch(IBPP::Exception& e)
{
wxMessageBox(e.what(),_T("Błąd zapisu zmian!"),wxOK | wxICON_EXCLAMATION, this);
}
W powyższym przykładzie jeśli tr1->Start(); zwróci wyjątek jego treść zostanie pokazana użytkownikowi w MessageBox (okienko dialogowe).
Opis klasy Baza3FrmApp.
Klasa ta jest wywoływana przy starcie aplikacji jako pierwsza i ona tworzy pierwsze okno i wczytuje splash screen. W zapisie jest bardzo krótka, posiada, bowiem tylko jeden atrybut i 2 metody. Jej definicja jest następująca:
class Baza3FrmApp : public wxApp
{
public:
bool OnInit();
int OnExit();
Baza3Frm * frame;
};
Pola klasy Baza3FrmApp.
Klasa posiada tylko jedno pole przechowujące wskaźnik do okna głównego programu.
Metody klasy Baza3FrmApp.
Klasa posiada 2 metody,pierwsza z nich OnInit() wywoływana jest przez polecenie:
IMPLEMENT_APP(Baza3FrmApp)
Metoda OnInit() jest odpowiedzialna za stworzenie okna i pokazanie go użytkownikowi, dodatkowo zaimplementowałem wczytywanie obrazka startowego.
Metoda OnExit() jest bardzo prosta po prostu zwraca 0 do systemu.
Opis klasy Baza3Frm.
Klasa ta implementuje główne okno programu, dziedziczy ona publiczne po klasie wxFrame, co jest domyślne dla wxDev-C++. W opisie postaram się skupić na funkcjonalności programu, a więc jego działaniu. Na początek omówię atrybuty klasy, które dodałem osobiście oraz ich znaczenie.
Pola klasy Baza3Frm.
W opisie atrybutów muszę rozdzielić atrybuty związane z biblioteką IBPP, które już omówiłem od innych kluczowych dla zrozumienia działania programu. Poniżej umieszczę listę atrybutów IBPP która w tej klasie jest najdłuższa (nie będę ich omawiał przy okazji kolejnych okien/klas):
const char* DbName;
std::string ServerName;
std::string UserName;
std::string Password;
IBPP::Database db1;
IBPP::Transaction tr1;
IBPP::Statement st1;
Lista kluczowych atrybutów klasy jest następująca:
MagazynDlg * frameM;
StanyMagazynoweDlg * frameS;
wxArrayString nazwyMagazynow;
int numerMagazynu;
bool otwartoBaze;
Atrybut: frameM ma za zadanie przechowywać wskaźnik do klasy magazynu, co uznałem za bardziej przejrzyste niż inicjowanie jej przy każdym użyciu.
Atrybut: frameS. Przez analogię do frameM można domyślić się, że przechowuje wskaźnik do okna Stanów Magazynowych.
Atrybut: nazwyMagazynów jak sama nazwa wskazuje przechowuje nazwy istniejących w bazie magazynów. Wszystkie metody operujące na bazie i magazynach zapisują do tej zmiennej nazwy aktualnie znajdujących się w bazie tablic.
Atrybut: numerMagazynu jest wykorzystywany przy opcji automatycznego nazywania tabel.
Atrybut otwartoBaze przechowuje informację o otwarciu(lub nie) jakiejkolwiek bazy danych. Jest to kluczowy atrybut gdyż od jego wartości zależy czy pewne operacje mogą zostać wykonane. Wartość ta jest zmieniana przy otwieraniu i zamykaniu bazy.
Metody klasy Baza3Frm.
Baza3Frm(wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("Baza3"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = Baza3Frm_STYLE);
Konstruktor klasy. Praktycznie bez zmian.
void Mnuzamknij1024Click(wxCommandEvent& event);
Zamykanie programu poprzez menu - zamknij.
void BZamknijClick(wxCommandEvent& event);
Zamykanie programu poprzez Button zamknij w prawym dolnym rogu okna.
void BNowyMagazynClick(wxCommandEvent& event);
Tworzenie nowego magazynu. Sprawdzane jest czy otwarta jest baza danych, czy nie wprowadzono zerowej nazwy magazynu, oraz czy nie istnieje już magazyn o tej nazwie. Tworzy strukturę kolumn w tablicy oraz otwiera okno magazynu.
void Mnuoprogramie1026Click(wxCommandEvent& event);
Menu - O programie - wyświetla MessageBox z podstawowymi informacjami o programie.
void Mnuoautorze1027Click(wxCommandEvent& event);
Menu - O autorze - wyświetla informacje o autorze programu.
void BWczytajBazeClick(wxCommandEvent& event);
Button WczytajBaze zamyka połączenie z bazą danych, jeśli jest jakaś otwarta oraz za pomocą metody OtworzBaze(file,false); otwiera wczytaną bazę danych.
void BNowaBazaClick(wxCommandEvent& event);
W działaniu podobne do poprzedniej, jednak sprawdza dodatkowo czy nazwa nowej bazy nie jest zerowa, oraz za pomocą OtworzBaze(file,true); tworzy nową bazę i łączy się z nią.
void OtworzMagazyn(wxString nazwaMagazynu);
Wykorzystując frameM tworzy okno magazynu oraz wyświetla je. Do nowego okna przekazywane są wskaźniki do obsługi bazy danych, a nazwa okna magazynu jest nazwą magazynu - tabeli w bazie danych.
void OtworzBaze(wxString path,bool tworzBaze);
Metoda przyjmuje 2 parametry: pierwszy to ścieżka do pliku z bazą danych, drugi to flaga, która jeśli jest prawdziwa sprawia, że metoda tworzy nowy plik bazy danych, w przeciwnym wypadku próbuje połączyć się z istniejącą. Jeśli operacja się powiodła ustawia atrybut otwartoBaze na 1. Wywołuje metodę WczytajMagazyny oraz przypisuje do listy magazynów: BListaMagazynow->Set(nazwyMagazynow) sprawiając, że użytkownik widzi w oknie głównym poprawną listę.
wxArrayString WczytajMagazyny(void);
Wczytuje z bazy listę tabel oraz zapisuje ją do zmiennej tego samego typu, co nazwyMagazynow.
void ZamknijBaze(void);
Zapisuje wszelkie zmiany dokonane na bazie danych i zamyka z nią połączenie ustawia atrybut otwartoBaze na 0.
void BZapiszBazeClick(wxCommandEvent& event);
Po prostu akceptuje transakcje.
void BOtworzMagazynClick(wxCommandEvent& event);
Otwiera okno Magazynu, pobiera pozycje i wartość(nazwę magazynu) z listy magazynów.
void BUsunMagazynClick(wxCommandEvent& event);
Wczytuje zaznaczoną pozycje i wykorzystując nazwę magazynu usuwa tabelę o tej nazwie.
void BStanyMagazynoweClick(wxCommandEvent& event);
Otwiera okno stanów magazynowych.
void OnClose(wxCloseEvent& event);
Domyślna metoda klasy. Dodałem jedynie wpis zamykający bazę danych.
void CreateGUIControls();
Domyślna metoda klasy. Wprowadzałem jedynie kosmetyczne zmiany.
Opis klasy MagazynDlg.
Opis całej klasy mogę rozpocząć od zamieszczenia kodu jej definicji, a w szczególności konstruktora. Pogrubioną czcionką zaznaczone są parametry związane z bazą danych przekazywane do klasy.
class MagazynDlg : public wxDialog
{
MagazynDlg(
IBPP::Database db1,
IBPP::Transaction tr1,
IBPP::Statement st1,
wxWindow *parent,
wxWindowID id = 1,
const xString title xT("Magazyn"),
const wxPoint& pos = wxDefaultPosition,
const xSize& size = wxDefaultSize,
long style = MagazynDlg_STYLE);
Pola klasy MagazynDlg.
FakturaDlg * frameF;
Jest jedynym atrybutem umieszczonym w pliku MagazynDlg.h. Zawiera wskaźnik do okna faktury.
MdNazwaMagazynu->SetLabel(title);
MdNazwaMagazynu jest statycznym napisem w oknie programu, jest jednak ważne gdyż przechowuje nazwę programu - nazwę tabeli/magazynu. Wartość inicjowana w konstruktorze.
Metody klasy MagazynDlg.
MagazynDlg(IBPP::Database db1,IBPP::Transaction tr1,IBPP::Statement st1,wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("Magazyn"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = MagazynDlg_STYLE);
Konstruktor został wzbogacony o 3 nowe parametry związane z bazą danych. Przygotowuje także WxGrid1 - tablicę widoczną w oknie by przyjęła ona określony wygląd, a następnie wczytuje do niej listę towarów.
void MdZamknijClick(wxCommandEvent& event);
Zamyka okno.
void MFakturaKupnaClick(wxCommandEvent& event);
Otwiera fakturę kupna. Gdy zostanie ona zamknięta ponownie wczytuje dane towarów.
void MFakturaSprzedazyClick(wxCommandEvent& event);
Otwiera fakturę sprzedaży. Po jej zamknięciu aktualizuje dane w WxGrid1.
bool WczytajStany(void);
Pobiera dane towarów do WxGrid1, wykorzystuje nazwę magazynu jako nazwę tablicy z której wczytuje dane.
void MOdswiezStanyClick(wxCommandEvent& event);
Wczytuje towary z bazy.
Opis klasy FakturaDlg.
Klasa ta jest bardzo zależna od parametru wywołania, ukrywa bowiem te elementy które nie powinny być widoczne. Od tego parametru zależy także jej główna funkcjonalność - dodawanie towarów - czyni to trochę metodę sprawdzającą towar zawiłą, jednakże główne jej zadanie jest łatwe do zrozumienia, oraz została ona skomentowana w każdym miejscu, które autor uznał za niejasne.
Początek konstruktora klasy:
FakturaDlg::FakturaDlg(
bool zakup,
IBPP::Database db,
IBPP::Transaction tr,
IBPP::Statement st[…]
Inicjacja obiektu w zależności od typu faktury.
Już w konstruktorze znajduje się parametr określający czy jest to faktura sprzedaży czy zakupu. W zależności od tego ukrywa wybrane kontrolki. W czasie akceptowania całej faktury przy sprzedaży, jeżeli nie ma towaru lub jest go niewystarczająca ilość dodawanie tego towaru zakończy się błędem. Odwrotnie jest w przypadku zakupu, każdy nowy towar jest po prostu dopisywany.
Pola klasy FakturaDlg.
wxColour color;
Kolor nieedytowalnych komórek tabeli
wxString nazwaMagazynu;
Atrybut zawiera nazwę magazynu.
bool kupno;
Jest to najważniejszy atrybut klasy, zależy od niego nie tylko wygląd, ale także zachowanie klasy.
wxString nazwaSymbol;
Wykorzystywane przy sprawdzaniu towaru, pomaga w porównaniach elementów.
Metody klasy FakturaDlg.
FakturaDlg(bool sp, IBPP::Database db,IBPP::Transaction tr,IBPP::Statement st,wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("Faktura"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = FakturaDlg_STYLE);
Podobnie jak w klasie magazynu w konstruktorze zainicjowany i skonfigurowany jest obiekt tabeli WxGrid1. W zależności od parametru sp diametralnie zmienia się wygląd okna.
void FAnulujClick(wxCommandEvent& event);
Cofa wszystkie zmiany(zachowując otwartą transakcję).
void FZaakceptujTowarClick(wxCommandEvent& event);
Oblicza na podstawie podanych danych ceny towaru(wszystkie nieedytowalne), oraz używając OdswiezDoZaplaty() odświeża wartości pól razem i do zapłaty.
void FUsunClick(wxCommandEvent& event);
Usuwa wiersz(towar) z zaznaczoną komórką.
bool NowyWiersz(void);
Wykorzystywana w innych metodach, dodaje nowy pusty wiersz.
void OdswiezDoZaplaty();
Odświeża wartości pól FRazem i FDoZaplaty obliczając te wartości na podstawie tablicy towarów.
bool SprawdzWypelnienie(void);
Metoda wykorzystywana przy zatwierdzaniu faktury, sprawdza czy użytkownik podał wszystkie dane.
void FZaakceptujClick(wxCommandEvent& event);
Zaakceptowanie całej faktury, w praktyce sprawdzenie czy sprzedawany towar znajduje się w tablicy bazy danych i uaktualnienie stanu ilości i ceny towaru. W przypadku zakupu nowych towarów także należy sprawdzić czy towar nie znajduje się w bazie, a jeśli tak to zwiększyć jego ilość.
bool SprawdzWMagazynie(wxString symbol);
Używana przez metodę akceptującą całą fakturę, wyszukuje podanego jako parametr symbolu w tablicy towarów.
bool DodajNowyTowar(wxString symbol,wxString nazwa,float ilosc,wxString jednostka,float cena,float wartosc);
Metoda dodaje dane towaru, którego nie ma w magazynie do tablicy magazynu w bazie danych. Używana tylko wtedy, gdy, nie ma tego towaru w magazynie.
bool UaktualnijMagazyn(bool sprzedaz, wxString symbol,wxString nazwa,float ilosc,wxString jednostka,float cena,float wartosc);
Metoda jest używana wtedy, gdy towar który należy dodać do magazynu już się w nim znajduje, jako pierwszy parametr metoda przyjmuje wartość faktury(kupno/sprzedaż) w zależności od tego albo dodaje do istniejącej pozycji dane towaru podane w parametrze, lub też odejmuje przy jego sprzedaży.
Opis klasy StanymagazynoweDlg.
Jest to bardzo prosta klasa, szczególnie w porównaniu z poprzednio omówionymi. Praktycznie nie ma sensu omawiać jej atrybutów, jedynie metody mogą okazać się trudniejsze.
Metody i atrybuty klasy StanyMagazynoweDlg.
StanyMagazynoweDlg(wxArrayString nazwyMagazynow,
IBPP::Database db1,
IBPP::Transaction tr1,
IBPP::Statement st1,
wxWindow *parent,
wxWindowID id = 1,
const wxString &title = wxT("Stany Magazynowe"),
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = StanyMagazynoweDlg_STYLE);
Do konstruktora przekazywana jest lista wszystkich tabel(magazynów).
bool WczytajStany(wxString nazwaMagazynu);
Metoda używa jako parametru nazwy magazynu, z którego ma wczytać towary. W klasie jest wywoływana dla każdego magazynu.
Podsumowanie.
Wszelkie niejasności powinny rozwiać załączona dokumentacja czy to do wxDev-C++, w którym znajduje się dokumentacja wxWidgets. Na płycie załączam dokumentację do IBPP, aczkolwiek jest ona dostępna w Internecie.