OFFICIUM INSERVIO
Your reliable partner for your business software...
Sage 100 Belege "HandleNew" (lBelIDTAN) bei neuen Belegen / Sage Document "HandleNew" with new documents
9.0
Freitag, 17. Januar 2025
Besondere Herausforderungen in der Praxis bei Legacy-Projekten
"Stolperfalle" in aktuellen Sage Versionen
Praxistauglicher Lösungsansatz über Benutzerfeld
Geeignete Sage-Schnittstellen , um die Lösung zu implementieren
Deutsch
Hintergrund
Das Sage-Handle "HandleNew" (COM/VBA "lBelIdTan") eines neu zu erstellenden Sage-Warenwirtschaftsbelegs steht seitens Sage leider nicht in allen Ebenen des AppDesigners sowie beim Interop-Vorgang über COM-Callable-Wrapper (CCW) und Runtime-Callable-Wrapper (RCW) durchgängig und zuverlässig auf allen UX/UI-Ebenen und Schnittstellen-Aufrufen persistent serialisiert zur Verfügung.
Dieses Handle entspricht datenbankseitig den korrespondierenden ID-KHKTAN-Datensätzen für die Belegtabellen "KHKVKBelege" und "KHKEKBelege".
D.h. Die Tan wird als ID in den Feldern "KHKVKBelege.BelID" für Verkaufsbelege und "KHKEKBelege.BelID" für Einkaufsbelege in der Microsoft-SQL-Server-Datenbank gespeichert.
Wird ein neuer Beleg erzeugt , so hat Sage dafür im Objektmodell des Beleges das Feld "HandleNew" (COM/VBA "lBelIdTan") vorgesehen , welches eigentlich den Abruf der zu erwartenden finalen Primärschlüssel-ID für das Datenbankfeld "BelID" (neben der Mandantennummer) erlauben soll.
D.h. beim Speichern des Beleges soll dann eigentlich die ID aus dem Feld "HandleNew" zu dem "Handle" des Beleges und somit zur datenbankseitigen "BelID" werden.
In der tatsächlichen Praxis ist das Feld "HandleNew" jedoch oft 0 , vor allem auch im VBA-Legacy Code , wenn über diesen mit der neuen Belegerfassung Aktionen stattfinden.
Für diverse Aktionen im Rahmen von Anwendungsentwicklungen ist es von großer Bedeutung , auch bereits für einen neuen Beleg einen eindeutigen Schlüssel zur Verfügung zu haben , beispielsweise um zusätzliche Belegdaten zu persistieren.
Eine besondere Herausforderung ist , dass ein Anwender einen neuen Beleg natürlich in der Sage 100 jederzeit verwerfen und nicht endgültig speichern könnte.
Es existieren mehrere Lösungsansätze für diese Anforderung, die im Folgenden dargelegt werden.
Besondere Herausforderungen in der Praxis bei Legacy-Projekten
In der Praxis gibt es in größeren Projekten häufig sehr umfangreichen Legacy-Code , beispielsweise auf Basis von Microsoft Access oder dem .NET Framework , der nicht unmittelbar auf die neue UX/UI-Technologie von Sage umgestellt , sondern aus diversen Gründen der Paxis erst nach und nach auf neue Technik umgestellt werden kann.
Solcher Full-Client-Legacy-Code erfordert oft bereits in verschiedenen Routinen bei neu erstellten Sage-Belegen eine korrekte und persistente Verknüpfung zum Beleg über einen eindeutigen Schlüssel.
Lösungsansätze
Die beschriebene Anforderung kann , wie so oft in der IT , auf unterschiedliche Weise gelöst werden.
Da Benutzerfelder (USER-Feld) des Beleges von Sage zuverlässig und durchgängig erhalten bleiben , d.h. erfolgreich durch alle UX/UI-Ebenen serialisiert werden , kann man diese nutzen , um eine eigene ID zu speichern und alle notwendigen Instanzen durchzureichen.
Dies könnte beispielsweise eine generierte GUID sein, die möglichst kompakt in dem Benutzerfeld abgelegt wird , oder eine eigene TAN , die über die Tabelle "KHKTan" verwaltet wird.
Optimal wäre es aber , wenn direkt das später endgültig verwendete Sage-Beleg-Handle bzw. "BelID" (die TAN des Belegs) zur Verfügung stünde , da sich auf diese Weise beispielsweise Referenzintegritäten über Foreign Keys, Trigger usw. in der SQL-Datenbank sehr einfach und performant umsetzen ließen.
Schließlich handelt es sich bei der "BelID"-TAN um den Primärschlüssel-Teil , der neben der Mandanten-ID , bereits im Clustered Index der Tabellen "KHKVKBelege" oder "KHKEKBelege" enthalten ist.
Interessant ist , dass Sage in aktuelleren Versionen die Funktion "SetHandleNew" am Belegobjekt anbietet.
Diese ermöglicht es tatsächlich , bereits im Voraus ein die Tan (normale KHKTAN) zu reservieren und zu setzen.
Nach dem Motto:
var tableName = Sagede.OfficeLine.Wawi.BelegBasic.BelegeTable.get_Belege( document.Erfassungsart , archiv: false );
var documentIdPreset = mandator.MainDevice.GetTan( tableName , mandator.Id );
document.SetHandleNew( documentIdPreset );
"Stolperfalle" in aktuellen Sage Versionen
Leider besteht bei diesem Ansatz in den aktuellen Sage-Versionen das eingangs genannte gravierende Grundproblem , dass Sage das Feld "HandleNew" nicht immer zuverlässig durch alle Serialisierungsschichten im Technologie-Stack über COM-Callable-Wrapper (CCW) und Runtime-Callable-Wrapper (RCW) sowie die diversen AppDesigner .NET-Assemblies in der UX/UI durchreicht.
Das bedeutet konkret , dass das Feld zwischendurch von Sage quasi einfach ignoriert wird und "in den Untiefen" des "mächtig aufgeblähten" Technologie-Stacks der Sage 100 irgendwo wieder auf "0" zurückgesetzt wird , auch wenn zuvor bereits irgendwo im Quellcode gesetzt.
D.h. auch wenn die TAN vorher z.B. in einer Beleg-Init DCM oder einem sonstigen geeigneten Schnittstellen-Einstiegspunkt bereits wie oben beschrieben vorbelegt wurde , dann geht diese Vorbelegung des Feldes "HandleNew" - je nach in der UX/UI ausgeführter Aktion - wieder verloren und das Feld "HandleNew" ist auf einmal wieder 0.
Im Gegensatz dazu bleiben die erwähnten Benutzerfelder (USER-Felder) de facto auf allen Serialisierungsebenen stets erhalten.
Somit bietet sich ein Lösungsansatz über das Benutzerfeld an.
Praxistauglicher Lösungsansatz über Benutzerfeld
Man legt sich im Sage Administrator Tool im entsprechenden Belegkopf ein Benutzerfeld an , Typ "Int32" bzw. "Int" , z.B. "USER_SAGDocId", und setzt dieses neue Feld auf schreibgeschützt. Schließlich sollen Anwender das Feld nicht verändern dürfen.
Nun muss man nur noch dafür sorgen , dass die vorher vergebene TAN (Code siehe oben) dann auch an der richtigen Stelle zunächst in das Benutzerfeld "USER_SAGDocId" geschrieben und dann vor(!) dem endgültigen Speichern durch Sage von dort wieder als Sage TAN über die bereits oben erwähnte Funktion "SetHandleNew" gesetzt wird.
Beispiel:
var documentIdPreset = document.UserProperties["USER_SAGDocId"].Value.ToInt32();
if ( document.Handle == 0
&& documentIdPreset > 0 )
{
document.SetHandleNew( documentIdPreset );
// Important! Reset user field now - important if the document is copied or transferred into other new documents!
document.UserProperties["USER_SAGDocId"].Value = 0;
}
Dieser Code sorgt dann kurz vor dem Speichern an der richtigen Stelle platziert dafür , dass die korrekte TAN benutzt wird , die auch vorher schon im Benutzerfeld gesetzt wurde.
⚠️Wichtig ist , dass nach erfolgreichem Setzen der TAN das Benutzerfeld wieder zurückgesetzt wird , da ein Beleg als Basis für eine Kopie oder einen Transfer benutzt werden kann , wodurch per Default der Inhalt des Benutzerfeldes kopiert wird.
Vorteile / Konsequenz:
Andere Programmteile können bei Bedarf im Vorfeld auch bei dem noch nicht gespeicherten Beleg bereits vorher schon über das Benutzerfeld auf die TAN zugreifen und damit Daten in eigene Tabellen persistieren , auch wenn der Beleg noch nicht endgültig gespeichert ist.
Wie schon erwähnt lässt sich mittels diverser SQL-Konstrukte problemlos die Datenintegrität gewährleisten und es lassen sich mittels SQL auch verwaiste Datensätze für nicht endgültig gespeicherte Belege automatisiert per SQL "aufräumen".
Erneut ein Blick auf die Praxis:
Im vielen vorhandenen umfangreichen Kundenprojekten mit sehr viele Legacy Code Fall wäre eine eigene ID (z.B. GUID) viel zu aufwändig , weil oft sehr viel Code vorhanden ist , der die echte Sage Beleg-TAN erwartet.
Somit ist der hier beschriebene Ansatz oft ein Ansatz , der am schnellsten zum Ziel führt , um auch älteren Legacy Code noch kompatibel zur neuen Belegerfassung zu halten.
Sicherlich muss es das erklärte Ziel sein , alten Legacy Code abzulösen und auf neue AppDesigner-Technik umzusetzen , aber mit der hier beschriebenen Vorgehensweise kann diese Umstellung in einem vertretbaren und planbaren Zeitrahmen geschehen.
Geeignete Sage-Schnittstellen , um die Lösung zu implementieren
Es sind verschiedene Sage Schnittstellen-Einstiegspunkte denkbar , z.B. bestimmte DLL-CommonMethods (DCMs) , mit denen man die o.g. endgültige Prüfung zum Setzen der reservierten TAN implementieren könnte.
Am geeignetsten sind DCMs , die kurz vor dem endgültigen Speichern des Beleges von Sage noch aktiviert werden (in der noch offenen Transaktion von Sage).
⚠️Es muss jedoch klar darauf hingewiesen werden , dass DCMs in der Praxis auch Probleme machen können.
Siehe ausführliche Hinweise hier:
In besonders "kritischen" i.S.v. wichtigen Projekten kommt man teils dann nicht umhin , den o.g. Workaround direkt an die entsprechende Stelle in der entsprechenden Sage .NET-Assembly zu platzieren.
Es hängt letzten Endes von der Relevanz des durchgeführten Projekts aus Kundensicht ab , ob dieser (sicherste) Weg eingeschlagen werden muss.