Cześć,
w program w których czasami muszę udowadniać że klient w trakcie używania sam narozrabiał a nie jest to problem systemu stosuję zapisywanie zmian jakie zaszły na formie. Robię to już od dawna i jakoś to działa chociaż kosztuje mnie to dużo pracy.
Wygląd to mnie więcej tak że w bazie tworzę tabelę o nazwie historia a w niej kilka kolumn mniej więcej takich jak:
- data_utworzenia (typu timestamp),
- uzytkownik (zalogowany do programu),
- uzytkownik_system (uzytkownik zalogowany do Windows),
- komputer (nazwa stanowiska Windows),
- panel (numer formy która "wpisuje" rekord do bazy),
- operacje (czyli zapisane otwartym tekstem historie zmian lub ich brak jeśli klient wycofa się ze zmian).
Tak naprawdę wiem wszystko i wiem jakie dane były wcześniej. W ten sposób mogę dokładnie prześledzić co się działo z danymi i kto rozrabiał.
Oczywiście wiem że takie rozwiązanie powoduje bardzo duży przyrost danych w bazie ale robię to z pełną świadomością i nie o to tutaj chodzi.
Gdy tworzony jest rekord taka operacja wygląda mniej więcej tak (przykład z ostatniego programu):
procedure Tnowy_model.dodaj;
var
licznik: integer;
historia: string;
begin
try
with DM.qrytemp, SQL do
begin
Close;
Clear;
Add('INSERT INTO modele (id_marka, model, opis, utworzyl)');
Add('VALUES (:id_marka, :model, :opis, :utworzyl)');
ParamByName('id_marka').AsInteger := id_marka;
ParamByName('model').AsString := edtnazwa.Text;
ParamByName('opis').AsString := mmoopis.Text;
ParamByName('utworzyl').AsString := main.uzytkownik;
ExecSQL;
end;
with DM.qrytemp, SQL do
begin
Close;
Clear;
Add('SELECT LAST_INSERT_ID()');
Open;
First;
licznik := DM.qrytemp.Fields[0].AsInteger;
end;
except
ShowMessage('Błąd! Nie dodano modelu. Sprawdź dane!');
end;
try
historia := 'Utworzenie modelu' + #13#10;
historia := historia + 'Nazwa: ' + edtnazwa.Text + #13#10;
historia := historia + 'Marka: ' + cbbmarka.Text + #13#10;
historia := historia + 'Opis: ' + mmoopis.Text + #13#10;
with DM.qryhistoria, SQL do
begin
Close;
Clear;
Add('INSERT INTO historia (panel, utworzyl, rekord, operacja, stanowisko) VALUES (:panel, :utworzyl, :rekord, :operacja, :stanowisko)');
ParamByName('panel').AsInteger := 2;
ParamByName('utworzyl').AsString := main.uzytkownik;
ParamByName('rekord').AsInteger := licznik;
ParamByName('operacja').AsString := historia;
ParamByName('stanowisko').AsString := main.komputer;
ExecSQL;
end;
except
ShowMessage('Błąd! Nie zapisano zmian w historii operacji!');
end;
with DM.qrymodele, SQL do
begin
Clear;
Close;
Add('select mo.id, mo.id_marka, mo.model, mo.opis, mo.usun, mo.data_utworzenia, mo.utworzyl, ma.marka from modele mo');
Add('inner join marki ma on mo.id_marka = ma.id where mo.usun = :usun order by ma.marka, mo.model');
ParamByName('usun').AsInteger := 0;
Open;
end;
DM.qrymodele.Locate('id', licznik, []);
end;
Nie kosztowało to zbyt wiele pracy ale też i panel zawierał tylko kilka pól edycyjnych.
W przypadku edycji już jest troszkę gorzej. Pokazując panel muszę najpierw uzupełnić dane w kontrolkach:
procedure Tmodele.btnedycjaClick(Sender: TObject);
begin
edit_model.cbbmarka.Items.Clear;
with DM.qrytemp, SQL do
begin
Close;
Clear;
Add('select marka from marki order by marka ASC');
Open;
First;
end;
while not DM.qrytemp.Eof do
begin
edit_model.cbbmarka.Items.Add(DM.qrytemp.FieldByName('marka').AsString);
DM.qrytemp.Next;
end;
edit_model.cbbmarka.ItemIndex := edit_model.cbbmarka.Items.IndexOf(DM.qrymodele.FieldByName('marka').AsString);
edit_model.id := DM.qrymodele.FieldByName('id').AsInteger;
edit_model.id_marka := DM.qrymodele.FieldByName('id').AsInteger;
edit_model.edtnazwa.Text := DM.qrymodele.FieldByName('model').AsString;
edit_model.mmoopis.Text := DM.qrymodele.FieldByName('opis').AsString;
edit_model.lbltworca.Caption := DM.qrymodele.FieldByName('utworzyl').AsString;
edit_model.lbldata_utworzenia.Caption := DateTimeToStr(DM.qrymodele.FieldByName('data_utworzenia').AsDateTime);
edit_model.Left := modele.Left + 20;
edit_model.Top := modele.Top + 20;
edit_model.Show;
end;
Następnie na formie edit_model tworzę zmienne dla historii:
private
h_marka, h_nazwa, h_opis: string;
Następnie w zdarzeniu OnShow zapamiętuję aktualny stan kontrolek:
procedure Tedit_model.FormShow(Sender: TObject);
begin
h_marka := cbbmarka.Text;
h_nazwa := edtnazwa.Text;
h_opis := mmoopis.Text;
edtnazwa.SetFocus;
end;
No i teraz jest już z górki, wystarczy w trakcie update rekordu użyć if-ów i wszystko staje się jasne:
procedure Tedit_model.btnzapiszClick(Sender: TObject);
var
historia: string;
begin
try
with DM.qrytemp, SQL do
begin
Close;
Clear;
Add('UPDATE modele SET id_marka=:id_marka, model=:model, opis=:opis WHERE id=:id');
ParamByName('id_marka').AsInteger := id_marka;
ParamByName('model').AsString := edtnazwa.Text;
ParamByName('opis').AsString := mmoopis.Text;
ParamByName('id').AsInteger := id;
ExecSQL;
end;
except
ShowMessage('Błąd! Nie zaktualizowano modelu. Sprawdź dane!');
end;
try
historia := 'Aktualizacja modelu' + #13#10;
if cbbmarka.Text <> h_marka then
historia := historia + 'Marka: było: ' + h_marka + ' jest: ' + cbbmarka.Text + #13#10;
if edtnazwa.Text <> h_nazwa then
historia := historia + 'Model: było: ' + h_nazwa + ' jest: ' + edtnazwa.Text + #13#10;
if mmoopis.Text <> h_opis then
historia := historia + 'Opis: było: ' + h_opis + ' jest: ' + mmoopis.Text + #13#10;
if ((edtnazwa.Text = h_nazwa) and (cbbmarka.Text = h_marka) and (mmoopis.Text = h_opis)) then
historia := historia + 'Nic nie zmieniono!';
with DM.qryhistoria, SQL do
begin
Close;
Clear;
Add('INSERT INTO historia (panel, utworzyl, rekord, operacja, stanowisko) VALUES (:panel, :utworzyl, :rekord, :operacja, :stanowisko)');
ParamByName('panel').AsInteger := 2;
ParamByName('utworzyl').AsString := main.uzytkownik;
ParamByName('rekord').AsInteger := id;
ParamByName('operacja').AsString := historia;
ParamByName('stanowisko').AsString := main.komputer;
ExecSQL;
end;
except
ShowMessage('Błąd! Nie zapisano zmian w historii operacji!');
end;
with DM.qrymodele, SQL do
begin
Clear;
Close;
Add('select mo.id, mo.id_marka, mo.model, mo.opis, mo.usun, mo.data_utworzenia, mo.utworzyl, ma.marka from modele mo');
Add('inner join marki ma on mo.id_marka = ma.id where mo.usun = :usun order by ma.marka, mo.model');
ParamByName('usun').AsInteger := 0;
Open;
end;
DM.qrymodele.Locate('id', id, []);
Close;
end;
Jak napisałem - jakoś to działa ale powiem szczerze że wyjątkowo mi się nie podoba. Po pierwsze te ify po drugie wszystko jest ok gdy pól edycyjnych jest nie więcej niż paręnaście (właśnie mam na pulpicie program w którym jest prawie 200 checkbox-ów).
Podzielcie się pomysłem jak to zrobić bardziej zgrabnie, może jakaś uniwersalna klasa która sama "wykryje" wszystkie pola edycyjne i zapamięta ich stan, następnie porówna przed opuszczeniem formy czy coś się zmieniło i zapisze zmiany? W taki powiedzmy sprytniejszy sposób ustawiam stan kontrolek na tej 200 elementowej formie:
for i := 0 to ComponentCount - 1 do
begin
if components[i] is TCheckBox then
TCheckBox(Components[i]).Checked := False;
if components[i] is TEdit then
TEdit(Components[i]).Text := '';
end;
A może jest jakiś gotowiec do tego a ja go nie znam?
Zapraszam do dyskusji.
Robert