AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

OnChange behindert Post

Ein Thema von ioster · begonnen am 15. Jan 2020 · letzter Beitrag vom 22. Jan 2020
Antwort Antwort
ioster

Registriert seit: 15. Aug 2008
41 Beiträge
 
Delphi 10.3 Rio
 
#1

OnChange behindert Post

  Alt 15. Jan 2020, 15:37
Datenbank: MS SQL Server • Version: 2008 • Zugriff über: FireDAC
Hallo zusammen,

nachdem ich grundlegende Probleme mit der FireDAC-Connection überwunden habe, ist nun bei der Entwicklung einer Belegmaske ein Fehler aufgetreten, der im Zusammenhang mit dem OnChange-Ereignis von TField angesiedelt ist.

Ich habe für den Beleg zwei Tabellen in denen Kopf- und Positionsdaten gespeichert werden. Im AfterOpen der Tabellen habe ich an das OnChange-Ereignis von bestimmten Feldern Prozeduren gehängt, um Adress- und Artikelstammdaten nachzulesen oder den Gesamtpreis von Position und Gesamtbeleg zu aktualisieren.

Delphi-Quellcode:
if Dataset.FindField('ArtNr') <> nil then
      Dataset.FieldByName('ArtNr').OnChange := ArtikelEingabe;

    if Dataset.FindField('VKNetto') <> nil then
      Dataset.FieldByName('VKNetto').OnChange := PreisBerechnung;
    if Dataset.FindField('Anzahl') <> nil then
      Dataset.FieldByName('Anzahl').OnChange := PreisBerechnung;
    if Dataset.FindField('Positionsrabatt1') <> nil then
      Dataset.FieldByName('Positionsrabatt1').OnChange := PreisBerechnung;
    if Dataset.FindField('Positionsrabatt2') <> nil then
      Dataset.FieldByName('Positionsrabatt2').OnChange := PreisBerechnung;
    if Dataset.FindField('Positionsrabatt3') <> nil then
      Dataset.FieldByName('Positionsrabatt3').OnChange := PreisBerechnung;
    if Dataset.FindField('Positionsrabatt4') <> nil then
      Dataset.FieldByName('Positionsrabatt4').OnChange := PreisBerechnung;
Die Preisberechnung funktioniert auch wunderbar. Allerdings habe ich das Problem, dass die Daten aus der Kopftabelle nicht richtig weggeschrieben werden. Die vorab eingegebenen Adressstammdaten und Belegsummen sind nach dem Navigieren in der Datenmenge weg.

Nun bin ich selber schon auf die Idee gekommen, den Status des Datasets zu prüfen und ein Post abzusenden, sofern die Datenmenge sich im dsedit oder dsinsert befindet. Das habe ich die Routine für die Preisberechnung eingebaut und nun auch in das OnBeforeInsert und OnBeforeEdit der Positionstabelle. Offenbar scheinen die Feldinhalte der Kopftabelle schon vorher abhanden gekommen zu sein. Auf dem Bildschirm werden die Werte solange richtig angezeigt bis man einen anderen Beleg aufruft.

Ein ähnliches Szenario mit 1:n-Beziehungen zwischen Tabellen habe ich auch in anderen Masken wie Artikel- und Adressstamm. Dort tritt dieses Problem allerdings nicht auf, aber an den Tabellen und Feldern hängen keine OnChange-Ereignisse.

Die Preisberechnung sieht folgendermaßen aus, wobei ich einräume, dass die ein oder andere Feinheit in der Berechnung fehlt. Das ist aber für die Speicherung der Daten nicht weiter relevant.

Delphi-Quellcode:
procedure TDatenmodul.Preisberechnung(Field: TField);
  var
    PosRab : double;
    PosZuschlag : double;
    VKNetto : double;
    Zaehler : integer;
    RabBetrag : double;
    ZuschlBetrag : double;
    MerkGesNetto : double;
    MerkGesBrutto : double;
    MerkUSt : double;
  begin
    RabBetrag := 0;
    ZuschlBetrag := 0;

    MerkGesNetto := Field.DataSet.FieldByName('VKNettoGesamt').AsFloat;
    MerkGesBrutto := Field.DataSet.FieldByName('VKBruttoGesamt').AsFloat;
    MerkUst := Field.DataSet.FieldByName('USt').AsFloat;

    with Field.DataSet Do
      begin
        VKNetto := Fieldbyname('Anzahl').asfloat * Fieldbyname('VKNetto').asfloat;

        for Zaehler := 1 to 4 do
          begin
            if (Fieldbyname('Positionsrabatt' + inttostr(Zaehler)).AsFloat <> 0) then
              begin
                if (Fieldbyname('Rabattart' + inttostr(Zaehler)).AsString = '=') then
                  PosRab := Fieldbyname('Positionsrabatt' + inttostr(Zaehler)).asfloat
                else
                  PosRab := VKNetto * Fieldbyname('Positionsrabatt' + inttostr(Zaehler)).AsFloat / 100;

                VKNetto := VKNetto - PosRab;

                RabBetrag := RabBetrag + PosRab;
              end;
          end;

        for Zaehler := 1 to 4 do
          begin
            if (Fieldbyname('Zuschlag' + inttostr(Zaehler)).AsFloat <> 0) then
              begin
                if (Fieldbyname('Zuschlagart' + inttostr(Zaehler)).AsString = '=') then
                  PosZuschlag := Fieldbyname('Zuschlag' + inttostr(Zaehler)).asfloat
                else
                  PosZuschlag := VKNetto * Fieldbyname('Zuschlag' + inttostr(Zaehler)).AsFloat / 100;

                VKNetto := VKNetto + PosZuschlag;

                ZuschlBetrag := PosZuschlag;
              end;
          end;

        if not(State in [dsedit, dsinsert]) then
          Edit;

        Fieldbyname('VKNettoGesamt').asfloat := VKNetto;
        Fieldbyname('RabattBetrag').AsFloat := RabBetrag;
        Fieldbyname('ZuschlagsBetrag').AsFloat := ZuschlBetrag;

        Fieldbyname('Ust').AsFloat := Fieldbyname('VKNettoGesamt').AsFloat * Fieldbyname('SteuerProzent').AsFloat / 100;
        Fieldbyname('VKBruttoGesamt').AsFloat := Fieldbyname('VKNettoGesamt').asfloat + Fieldbyname('Ust').asfloat;

        Post;
      end;

    with Datenmodul.AngebotKopf Do
      begin
        Edit;
        Fieldbyname('VKNetto').AsFloat := Datenmodul.AngebotKopf.Fieldbyname('VKNetto').AsFloat - MerkGesNetto + Field.DataSet.FieldByName('VKNettoGesamt').asfloat;
        Fieldbyname('VKBrutto').AsFloat := Datenmodul.AngebotKopf.Fieldbyname('VKBrutto').AsFloat - MerkGesBrutto + Field.DataSet.FieldByName('VKBruttoGesamt').asfloat;
        Fieldbyname('Ust').AsFloat := Datenmodul.AngebotKopf.Fieldbyname('USt').AsFloat - MerkUST + Field.DataSet.FieldByName('USt').asfloat;

        if Field.Dataset.FieldByName('Steuerschluessel').Asinteger > 0 then
          Fieldbyname('USt' + inttostr(Field.Dataset.FieldByName('Steuerschluessel').Asinteger)).asfloat := Fieldbyname('USt' + inttostr(Field.Dataset.FieldByName('Steuerschluessel').Asinteger)).asfloat - MerkUSt + Field.DataSet.FieldByName('USt').asfloat;

        Post;
      end;
  end;
Viele Grüße
Ingo
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
7.605 Beiträge
 
Delphi XE4 Professional
 
#2

AW: OnChange behindert Post

  Alt 21. Jan 2020, 17:54
Hallo,
Zitat:
Die vorab eingegebenen Adressstammdaten und Belegsummen sind nach dem Navigieren in der Datenmenge weg.
Das DataSet.Post schließt ja das DataSet.
Ich würde zum Speichern eine eigene Query benutzen.
Heiko
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
7.430 Beiträge
 
Delphi 10.3 Rio
 
#3

AW: OnChange behindert Post

  Alt 21. Jan 2020, 20:45
Das DataSet.Post schließt ja das DataSet.
Wie kommst du denn da drauf?

Ich würde zum Speichern eine eigene Query benutzen.
Das ist nicht nötig. Die TFDQuery ist sehr wohl in der Lage, das selbst auszuführen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
35.409 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: OnChange behindert Post

  Alt 21. Jan 2020, 22:24
Beim Post wird, entsprechend der geänderten Felder (TField: OldValue<>Value) und dem DataSet-State ein Statement generiert, unter Bezug auf die FieldInfos des Queries.
Bei Insert+Post ein INSERT INTO, bei Edit+Post ein UPDATE und bei Delete natürlich DELETE FROM.

Erstmal wäre es besser, wenn die QueryKomponente das ID/Key/Index-Feld(er) des Datensatz kennen, um ein optimales WHERE genierien zu können.
Und manchmal kann die Code-Generation auch schief laufen.
Schön ist, dass bei Problemen oft garkeine Fehlermeldung kommt und dann garnichts gemacht wird.
Tipp: Hänge einen SQL-Monitor an die Connection oder schaue im Activity-Log der Datenbank, ob und was für ein SQL-Statment beim Post abgeschickt wird.

Aus diesem Grund kann man hier bei vielen DB-Komponenten optional den Tabellennamen angeben, falls er nicht automatisch erkannt wird,
und neben dem SQL kann man dann auch ein InsertSQL, UpdateSQL und DeleteSQL angeben, wenn eines/alle dieser SQL nicht automatich aus dem SQL (SELECT) generiert wird.

Delphi-Referenz durchsuchenTFDQuery.Table
Delphi-Referenz durchsuchenTFDQuery.UpdateObject
Delphi-Referenz durchsuchenTFDQuery.KeyFieldCount


Und nach dem Post wird eventuell noch ein UpdateRecord ausgeführt, wobei dort das WHERE des Sql (SELECT) ersetzt wird, durch das/die KeyFields,
um die aktuellen Inhalte des grade geposteten Datensatzes zu bekommen, falls z.B. ein Trigger dort anschließend nochmal was geändert hat.
Kommt hier kein Result (RecordCount=0) zurück, dann wird dieser Datensatz natürlich gelöscht, da er scheinbar auf der DB nicht mehr existiert (durch Trigger gelöscht).
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (21. Jan 2020 um 22:29 Uhr)
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
7.605 Beiträge
 
Delphi XE4 Professional
 
#5

AW: OnChange behindert Post

  Alt 21. Jan 2020, 23:03
Hallo,
Zitat:
Wie kommst du denn da drauf?
Alles schon vorgekommen...

Das Zusammenspiel datensensitiver Elemente beim DML und
bei gleichzeitiger Nutzung zur Anzeige führt doch nur zu Ärger.
Und debuggbar ist das meistens nicht.

Ich mache da aus eigenen Erfahrungen einen Bogen darum.
Heiko
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
7.430 Beiträge
 
Delphi 10.3 Rio
 
#6

AW: OnChange behindert Post

  Alt 22. Jan 2020, 09:33
Das Zusammenspiel datensensitiver Elemente beim DML und
bei gleichzeitiger Nutzung zur Anzeige führt doch nur zu Ärger.
Und debuggbar ist das meistens nicht.
Das kann ich schlicht und einfach nicht bestätigen. Ich benutze datensensitive Controls und Datasets mit Append, Edit, Post, Delete seit Jahren ohne irgendwelche Probleme. Wie bei jeder anderen Technologie muss man halt wissen, wie man damit umgehen muss. Probleme gibt es im Gegenteil eher mit solchen Anwendungen, die das um alles in der Welt vermeiden wollen.

Aber das geht hier schon etwas am Thema vorbei.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
7.430 Beiträge
 
Delphi 10.3 Rio
 
#7

AW: OnChange behindert Post

  Alt 22. Jan 2020, 09:42
Im AfterOpen der Tabellen habe ich an das OnChange-Ereignis von bestimmten Feldern Prozeduren gehängt, um Adress- und Artikelstammdaten nachzulesen oder den Gesamtpreis von Position und Gesamtbeleg zu aktualisieren.
Das ist vermutlich die falsche Vorgehensweise. Ein Post auf ein DataSet innerhalb eines OnChange-Events eines Fields dieses DataSets kann nicht funktionieren!
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
35.409 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: OnChange behindert Post

  Alt 22. Jan 2020, 10:43
Das OnChange der sichtbaren Controls reagiert ja, wenn sich der Inhalt dieses Controls ändert
und das passiert auch z.B. beim Open, Close, Scroll, Refresh des DataSets und dort sollte sowas wie Edit/Insert/Post/Delete in einem Event besser nicht vorkommen.

Warum werden die Berechnungen nicht in einem Trigger in der Datenbank erledigt, bzw. im BeforePost, schon beim Speichern des Datensatzes?
Auch als CalcField oder schon im SELECT können während des Ladens Werte berechnet werden.

Im TField.OnChange sollte auf dessen DataSet auch niemals "Post" ausgeführt werden.

Einfaches Beispiel:
FieldA.OnChange ändert FieldB und FieldC und macht anschließend Post
* FieldB.OnChange ändert auch irgendwas und macht ebenfalls Post
weiter im FieldA.OnChange, kommt nun FieldC dran ... was nicht geht, weil schon Post schon durch ist
und immernoch im FieldA.OnChange, käme nun das POST, was oben ausgegraut wurde, da sich das nächste Event dazwischen schob
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (22. Jan 2020 um 10:57 Uhr)
  Mit Zitat antworten Zitat
jobo

Registriert seit: 29. Nov 2010
2.783 Beiträge
 
Delphi 2010 Enterprise
 
#9

AW: OnChange behindert Post

  Alt 22. Jan 2020, 10:46
Mir scheint die Preisberechnung etwas "verwickelt". Sie ist an recht vielen Stellen eingebunden und verursacht gefühlt dutzende Posts. Und ist tatsächlich sichergestellt, dass die Kopfdaten (Dataset) "richtig steht", also synchron zu dem "Field" Dataset?

Außerdem werden hier offenbar redundante Infos abgelegt, die man problemlos während der Anzeige als berechnete Felder liefern kann oder täusche ich mich.

Das Ziel wäre m.E. eine vom Dataset(s) entkoppelte Berechnung über Paramter und das einmalige Auslösen dieser Berechnung, sowie einmalige Speichern der Berechnung.
Gruß, Jo
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
7.605 Beiträge
 
Delphi XE4 Professional
 
#10

AW: OnChange behindert Post

  Alt 22. Jan 2020, 18:46
Hallo,
Zitat:
Wie bei jeder anderen Technologie muss man halt wissen, wie man damit umgehen muss
Da hast du vollkommen Recht!
Deshalb lasse ich es ja sein
Heiko
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:43 Uhr.
Powered by vBulletin® Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2019 by Daniel R. Wolf