Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 08:57

vandyke hat geschrieben:...Speichere doch das Flag in der Singleinstanz CU in einem TempRec. Sollte doch beim Rollback rückgängig gemacht werden....

Funktioniert das? Wieder was gelernt. Das fände ich eine gute Lösung.
Edit:
Another misconception about temporary records is that they are managed as transactions. (...) if you call ERROR, it won’t rollback any inserts, changes, or deletions from your temporary tables.
Quelle

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 09:04

m_schneider hat geschrieben:indem du die Function SynchronizeFields in eine eigene CU ausgliederst
Mein erster Versuch war natürlich, die eigentliche Synchronisation in eine eigene Codeunit auszulagern und diese in dem EventSubscriber mit IF Codeunit.RUN zu starten.
Das funktionierte in diesem Fall (Änderung eines Feldes in der Verkaufszeile) hervorragend. :-D

Wenige Minuten später erhielt ich dann von NAV (bei einer völlig anderen Aktion) die Meldung, dass IF Codeunit.RUN in Schreibtransaktionen nicht erlaubt ist. :cry:
Verdammt! Also alles wieder zurück.

m_schneider hat geschrieben:Was ich mich frage, warum funktioniert es bei dem einen Feld und bei dem anderen nicht?
Weil nur bei diesem Feld eine Synchronisation mit Validate in das Zielfeld erfolgt, welches im OnValidate der Zieltabelle eine Prüfung hat, die einen Error auslösen kann.
Andere Felder mit einer solchen Prüfung werden nicht von der Verkaufszeile per Synchronisation aktualisiert.

vandyke hat geschrieben:Also wird doch in beide Richtungen synchronisiert? Oder von der Zieltabelle in eine weitere Zieltabelle, ..., die dann irgendwann wieder zur Herkunftstabelle synchronisieren will?
Es werden Feldwerte von der Verkaufszeile in die Arbeitsauftragszeile synchronisiert, aber auch (andere Feldwerte) von der Arbeitsauftragszeile in die Verkaufszeile.
Aus dem Grund muss ich mit dem Flag sicherstellen, dass die Synchronisation von der Verkaufszeile in die Arbeitsauftragszeile nicht die Synchronisation von der Arbeitsauftragszeile in die Verkaufszeile anstößt, da dies ansonsten wiederum die Synchronisation der Verkaufszeile in die Arbeitsauftragszeile anstoßen würde, war wiederum ...

vandyke hat geschrieben:Speichere doch das Flag in der Singleinstanz CU in einem TempRec. Sollte doch beim Rollback rückgängig gemacht werden.
Das mache ich ja so schon von Anfang an (siehe Screenshot1.png). Leider wird der Eintrag für 'SYNCHRONIZATION' in der TempTabelle bei einem Rollback nicht rückgängig gemacht und verbleibt dort somit mit seinem zugewiesenen Wert 'ACTIVE'.
Anscheinend gilt der Rollback nicht für Daten in temporären Tabellen.

m_schneider hat geschrieben:Oder eventuell mit meinen angesprochenen Try-Functions.
vandyke hat geschrieben:Oder halt, wie hier schon erwähnt, mit der TryFunction.
Ihr habt das böse Wort (TryFunction) gesagt! :lol:
Nach dem Beitrag Splitting Atoms with TryFunction traue ich mich nicht, die Synchronisation in eine TryFunction auszulagern, da die Synchronisation von dem zentralsten Event ausgelöst wird, dass man sich in der Datenbank vorstellen kann (OnAfterOnDatabaseModify).
Sollte die Synchronisation erfolgreich sein (was sie ja in 99,9% aller Fälle ist) und ein anderer (nachgelagerter) Programmcode die Änderung der Herkunftstabelle verbieten, dann würden die synchronisierten Daten in den Zieltabellen nicht rückgängig gemacht werden!

Beispiel:
Erhöhung der Menge in der Verkaufszeile von 1 auf 5.
Die neue Menge wird (erfolgreich) in die Arbeitsauftragszeile synchronisiert, da die Arbeitsaufträge noch nicht in Bearbeitung sind (Status = <leer>).
Jetzt stellt NAV (Standard oder durch irgendein anderes Event auf OnAfterOnDatabaseModify) nach der Synchronisation fest, dass die Änderung der Menge nicht zulässig ist und macht einen Rollback.
In der Verkaufszeile steht nun wieder die ursprüngliche Menge (1), während in der Arbeitsauftragszeile die Menge 5 stehen bleibt, da dies durch eine TryFunction dort eingetragen wurde. :shock:

Theoretischer Lösungsansatz:
Ich überlege schon, ob ich die Synchronisations-Einrichtung um ein Feld erweitere, in welchem ich Prüf-Routinen eintragen kann.
In meinem EventSubscriber rufe ich dann vor dem Aufruf der Funktion SynchronizeRecord(RecRef) eine Funktion CheckSynchronization(RecRef) auf, welche in der Synchronisations-Einrichtung nach Prüf-Routinen sucht und diese ausführt.
So könnte ich in der Theorie den Aufruf von SynchronizeRecord(RecRef) unterdrücken, da ich ja schon vorher wüsste, dass die Synchronisation gegen die Wand fahren würde.
Ich mache mir bei diesem Lösungsansatz jedoch Sorgen, dass dies stark zu Lasten der Performance aller Transaktionen gehen könnte.

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 09:49

darf ich fragen, warum du den Wert, ob eine Synchro ausgeführt wird, oder nicht, in einem Feld einer tempTable speicherst, oder habe ich das total falsch verstanden?

Timo Lässer hat geschrieben:Anscheinend gilt der Rollback nicht für Daten in temporären Tabellen.

korrekt -> http://www.tharangac.com/2017/02/understanding-commit-and-transaction.html

Also dein SyncMgt reagiert auf OnDatabaseModify --> prüft, ob Table überhaupt in SynchEinrichtung vorhanden --> syncht ggf. Felder

du setzt in einer SI eine glob Variable (keinen Record) -> SyncActive, bevor deine SyncFields oder SyncTable aufgrufen wird, damit weißt du, ob der Sync gerade aktiv ist und vermeidest so Endlosschleifen (prüfung darauf natürlich vor SyncTable).

Kommt es zum Fehler, findet das Rollback statt - was ja auch wieder OnDataBaseModify anstößt, oder?
Wenn also in deiner Sync-Function geprüft wird, ob SyncActive, dann muss SyncActive vor dem EXIT wieder in der SI auf false gesetzt werden....

Code:
....
SyncTable.Reset;
....
IF SyncTable.FINDSET(FALSE) THEN BEGIN
  IF GlobalVarMgt.GETSYNC() THEN BEGIN
    GlobalVarMgt.SETSYNC(FALSE);
    EXIT;
  END
  ELSE
    GlobalVarMgt.SETSYNC(TRUE);
   REPEAT
     //doSomeThings
  UNTIL SynCTable.NEXT = 0;
END;


nur so als blöde Idee

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 10:39

sweikelt hat geschrieben:darf ich fragen, warum du den Wert, ob eine Synchro ausgeführt wird, oder nicht, in einem Feld einer tempTable speicherst, oder habe ich das total falsch verstanden?
Anfangs (im Jahr 2006) hatte ich in meiner SI-Codeunit für jede "anwendungsglobale" Variable
a) je eine globale Variable
b) je eine SetVariable-Funktion
c) je eine GetVariable-Funktion

Im Laufe der Zeit wurden das immer mehr Variablen und Funktionen, so dass ich mich schon im Jahr 2009 entschloss, die Variablen in einer temporären Tabelle zu speichern.
Dadurch brauchte ich in der SI-Codeunit nur noch
a) eine globale, temporäre Tabelle
b) eine einzige SetVariable-Funktion
c) eine einzige GetVariable-Funktion
und konnte damit unendlich viele Variablen als "anwendungsglobale" Variablen verwalten.

sweikelt hat geschrieben:
Timo Lässer hat geschrieben:Anscheinend gilt der Rollback nicht für Daten in temporären Tabellen.
korrekt -> http://www.tharangac.com/2017/02/unders ... ction.html
Ich habe gerade versucht, ob sich etwas ändern würde, wenn ich für diesen konkreten Fall den Wert nicht über die universelle Set-/Get-Funktion in der temporären Tabelle, sondern "klassisch" in einer eigenständigen Variable in der SI-Codeunit ablege.
Leider wird auch der Wert in der globalen Variable der SI-Codeunit bei einem Rollback nicht zurückgesetzt. :cry:

sweikelt hat geschrieben:Kommt es zum Fehler, findet das Rollback statt - was ja auch wieder OnDataBaseModify anstößt, oder?
Nein, durch das Rollback wird der OnAfterOnDatabaseModify nicht erneut ausgelöst. Der Anwender bleibt nur auf dem ungespeicherten Datensatz stehen.
Erst, wenn er erneut versucht, den Datensatz zu verlassen (oder F5 drückt), möchte NAV diesen Datensatz speichern und löst OnAfterOnDatabaseModify aus.

sweikelt hat geschrieben:Wenn also in deiner Sync-Function geprüft wird, ob SyncActive, dann muss SyncActive vor dem EXIT wieder in der SI auf false gesetzt werden....
Klingt leider nur im ersten Moment nach einer möglichen Lösung.
Das Problem ist, dass während der Synchronisation der Wert von dem Herkunftsdatensatz in mehrere Zieldatensätze in einer oder mehreren Zieltabelle(n) übertragen werden kann (und in der Realität auch wird).
Würde eine Abfrage auf SyncActive das Flag wieder zurücksetzen, dann würde der erste Modify auf einer Zieltabelle zwar keine weitere Synchronisation auslösen, aber schon ab dem zweiten Modify würde die Synchronisation eine Endlosschleife anstoßen.

Auch wenn wir bisher noch keine Lösung für dieses Problem gefunden haben, so möchte ich mich trotzdem jetzt schon einmal bei euch allen für die vielen wertvollen Vorschläge bedanken.
Vielleicht hat einer von euch ja doch noch die geniale Idee, wie man NAV "austricksen" kann.

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 10:52

Timo Lässer hat geschrieben:Nein, durch das Rollback wird der OnAfterOnDatabaseModify nicht erneut ausgelöst. Der Anwender bleibt nur auf dem ungespeicherten Datensatz stehen.
Erst, wenn er erneut versucht, den Datensatz zu verlassen (oder F5 drückt), möchte NAV diesen Datensatz speichern und löst OnAfterOnDatabaseModify aus.
--> ah, dann geht es natürlich nicht :'(

Timo Lässer hat geschrieben:Das Problem ist, dass während der Synchronisation der Wert von dem Herkunftsdatensatz in mehrere Zieldatensätze in einer oder mehreren Zieltabelle(n) übertragen werden kann (und in der Realität auch wird).
Würde eine Abfrage auf SyncActive das Flag wieder zurücksetzen, dann würde der erste Modify auf einer Zieltabelle zwar keine weitere Synchronisation auslösen, aber schon ab dem zweiten Modify würde die Synchronisation eine Endlosschleife anstoßen.

--> dann klappt das defintiv so auch nicht - hatte zu kurz gedacht

wir geben noch nicht auf ;)

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 14:18

Timo Lässer hat geschrieben:
sweikelt hat geschrieben:
Timo Lässer hat geschrieben:Anscheinend gilt der Rollback nicht für Daten in temporären Tabellen.
korrekt -> http://www.tharangac.com/2017/02/unders ... ction.html
Ich habe gerade versucht, ob sich etwas ändern würde, wenn ich für diesen konkreten Fall den Wert nicht über die universelle Set-/Get-Funktion in der temporären Tabelle, sondern "klassisch" in einer eigenständigen Variable in der SI-Codeunit ablege.
Leider wird auch der Wert in der globalen Variable der SI-Codeunit bei einem Rollback nicht zurückgesetzt. :cry:

Dann bleibt ja eigentlich nur eine echte Tabelle mit echtem Datensatz. Der wird dann auch zurückgesetzt.

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 14:40

m_schneider hat geschrieben:Dann bleibt ja eigentlich nur eine echte Tabelle mit echtem Datensatz. Der wird dann auch zurückgesetzt.
Auch wenn es mir schwer fällt, "session-abhängige", kurzlebige Daten physikalisch in eine Tabelle zu schreiben, so ist das die einfachste und universellste Lösung für dieses Problem!

DANKE!

Michael, du hast dir beim nächsten Community-Treffen ein großes Bier verdient!

Re: Kein Rollback bei Error nach EventSubscriber

30. April 2020 14:50

Timo Lässer hat geschrieben:
m_schneider hat geschrieben:Dann bleibt ja eigentlich nur eine echte Tabelle mit echtem Datensatz. Der wird dann auch zurückgesetzt.
Auch wenn es mir schwer fällt, "session-abhängige", kurzlebige Daten physikalisch in eine Tabelle zu schreiben, so ist das die einfachste und universellste Lösung für dieses Problem!

Mir auch. Aber manchmal gibt es keine andere Möglichkeit. Das ist das Problem an der "einfachen" Transaktionssteuerung. Ist mir aber so lieber als mich ala SQL-Server alles selbst zu managen.

Timo Lässer hat geschrieben:Michael, du hast dir beim nächsten Community-Treffen ein großes Bier verdient!

Danke danke. Ich komme darauf zurück.

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

30. April 2020 17:29

Das mit der TempTable wusste ich leider gar nicht. Schade.

Noch einen kleinen Vorschlag habe ich. Ist vielleicht aber weniger praktikabel, weil es das eigentliche Problem nicht lösen wird und nur verschiebt. Es geht darum, schon vor der synch zu prüfen. Vielleicht auch, wenn mal in bestimmten ZielfeldValidateTriggern die Prüfung auf den Status "vergessen" wurde. In deinem Fall gehts ja erstmal hauptsächlich nur um den Status des Zielbelegs. Insofern könntest du auch in der Einrichtungstabelle ein Feld anlegen indem du einen Belegfilter definieren kanns. Z.B.: "Belegart: Angebot|Auftrag, Status: <5".

Gruß

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

4. Mai 2020 09:43

Die Idee, bei den jeweiligen Zieltabellennummern frei definierbare Funktionsnamen zu hinterlegen, die dann vor der eigentlichen Synchronisation durchlaufen würden, kam mir auch schon in den Kopf, jedoch wäre das ein sehr umfangreicher Programmieraufwand.
Da ist die Umstellung der "anwendungsglobalen Variable" von einer TempTabelle auf einen tatsächlichen Datenbankzugriff bedeutend einfacher.

Ein Zieltabellenfilter würde das Problem nur "ausblenden", da die Synchronisation durch den Filter die Arbeitsaufträge nicht mehr sehen würde.
Die Synchronisation "glaubt" also, alles wäre in Ordnung und lässt die Änderung in der Herkunftstabelle zu (argh!) und synchronisiert diese Änderung dann auch noch nicht einmal in die Zieltabelle (argh!)
Ergebnis: Der Verkaufsauftrag enthält eine unzulässige Änderung, von der die Montage-Abteilung nichts mitbekommt.

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

4. Mai 2020 13:20

Ja, das mit de globalen Variable ist einfacher und wahrscheinlich sowieso nötig.

Mir ging es auch nur darum, ob es sich nicht vielleicht für die Zukunft lohnt. Denn es hieß auch, dass oft flexibel angepasst wird. Dann ist vielleicht nicht schon bei Status::x schluss, sondern erst bei y. Das müsste dann ja wieder im Code geändert werden. Schneller gehts halt mit der EinrichtungSyncTabelle. Ich meinte auch nicht, dass du mit einem Belegfilter die Synchro ausblendest, sondern mit diesem prüfst, ob schon im Vorfeld ein Error geworfen werden soll.

Ein Beispiel:
Die Einrichtungstabelle enthält für die Zieltabelle (Bsp: 37) noch die Felder Kopftabelle=36 und Kopfbelegfilter=Status: x

Holen des Kopfdatensatzes (Nicht getestet. Habe meine eigene Funktion SetRecRefLineFromHeader nur "umgedreht")
Code:
LOCAL SetRecRefHeaderFromLine(VAR RecRefHeader : RecordRef;VAR RecRefLine : RecordRef;HeaderTableID : Integer)

RecRefLine.SETRECFILTER;

IF HeaderTableID = 0 THEN
  HeaderTableID := RecRefLine.NUMBER - 1;

RecRefHeader.OPEN(HeaderTableID);

FOR i := 1 TO RecRefHeader.KEYINDEX(1).FIELDCOUNT DO BEGIN
  FieldID := RecRefHeader.KEYINDEX(1).FIELDINDEX(i).NUMBER;
  FldRef := RecRefHeader.FIELD(FieldID);
  FldRef.SETRANGE(RecRefLine.FIELD(FieldID));
END;


Prüfen, ob der Kopfdatensatz im Filter ist:
Code:
RecordIsInFilterString(RecRef : RecordRef;FilterString: Text) : Boolean

IF FilterString = '' THEN
  EXIT(TRUE);

RecRefCopy.SETRECFILTER;
ApplyFilters(RecRefCopy,FilterString);

IF RecRefCopy.ISEMPTY THEN
  EXIT(FALSE);

EXIT(TRUE);


Das sind die Hauptfunktionen. Die Schwierigkeit ist die Funktion ApplyFilters und die dortige IsFilter.

Kann man sich hier besorgen. Ich hab da selbst mal was gefunden und erweitert/korrigiert:
viewtopic.php?f=17&t=32674

Ich mache es genauso für meine Feldberechtigung. (Nur das ich da nicht vom Zeilen Record den Kopf besorgen muss)

Grüße

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

4. Mai 2020 13:37

Ah, jetzt verstehe ich:
Man prüft nicht den Wert eines Feldes, sondern ob der (gefundene) Datensatz auch noch nach der Anwendung eines bestimmten Filters gefunden werden könnte. (Gefunden=OK / Nicht gefunden=Error)
Diese Denkweise kenne ich von anderen "generischen" Modulen, in denen Kriterien durch geschickte Filterung geprüft werden können.

Funktionen, um für eine Tabelle einen Filter zu definieren, speichern und anzuwenden, haben wir auch in unserer Datenbank.
Wir bauen hier auf Funktionen vom Object Manager Advanced auf.
Darüber hinaus haben wir uns auch eine Funktion gebaut, um schnell herauszufinden, ob ein Datensatz (den man schon im Zugriff hat), in eine Filterbedingung fällt.

Die "Header-Tabelle" ist aber nicht immer die Tabelle mit "Table No. - 1". ;-)
Siehe "Service Header" (T5900), "Service Item Line" (T5901) und "Service Line" (T5902).
Aus diesem Grund haben wir noch eine Setup-Tabelle, in welcher wir die Tabellenrelationen zwischen ausgewählten Header- und Line-Tabellen definieren können.
Mit dieser Tabelle können wir dann schnell den Kopf zu einer Zeile bzw. die Zeilen zu einem Kopf per RecordRef ermitteln.

Fazit:
Dieser Lösungsansatz ist in unserem konkreten Fall zwar jetzt "mit Kanonen auf Spatzen schießen", gefällt mir aber und könnte eventuell sehr hilfreich sein.
Die dazu erforderlichen "Werkzeuge" (Funktionen) sind bei uns ja schon aufgrund anderer Anforderungen vorhanden.

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

8. Mai 2020 08:43

Timo Lässer hat geschrieben:Die "Header-Tabelle" ist aber nicht immer die Tabelle mit "Table No. - 1". ;-)

Das stimmt natürlich. Deswegen nimmt ja die gezeigte Funktion auch als Parameter die TableID auf :D

Re: [Gelöst] Kein Rollback bei Error nach EventSubscriber

8. Mai 2020 08:52

vandyke hat geschrieben:Deswegen nimmt ja die gezeigte Funktion auch als Parameter die TableID auf :D
Upps! Wer lesen kann, ist klar im Vorteil. :oops: