top of page

Schreibende Listen / Writable lists

(alle Versionen/all versions)

Donnerstag, 20. April 2023

Hintergrund / Background

"Stolperfalle" / "Pitfall" - Fehler im Sage-Assistenten / Bugs with the Sage wizard

Manuelles Erstellen der "Schreibenden Liste" / How to build the list manually

Ungünstige Primärschlüssel / Adverse primary keys

Empfohlener Primärschlüssel / Recommended primary key

Primärschlüssel-Felder nicht schreibschützen / Do not set primary key fields to read-only

Performance



Hintergrund / Background


"Schreibende Listen" im Sage AppDesigner können mit einem Assistenten über rechte Maustaste auf AppDesigner-Listen eingerichtet werden.

Eine "Stolperfalle" in diesem Zusammenhang ist, dass die Komponente "Schreibende Listen" wenig Sinn bei Tabellenstrukturen mit zusammengesetzten Primärschlüsseln macht.

Außerdem sind die Sage Assistenten für Schreibende Listen zum Teil nicht zu gebrauchen, da diese fehlerbehaftet sind, wenn größere Kundendatenbanken zum Einsatz kommen.


"Schreibende Listen" (writable lists) in the Sage AppDesigner can be set up with a wizard using the right mouse button on AppDesigner lists.

A "pitfall" in this context is that the component "Schreibende Listen" makes little sense for table structures with composite primary keys.

In addition, some of the Sage assistants for writing lists cannot be used because they are prone to errors when larger customer databases are used.



"Stolperfalle" / "Pitfall" - Fehler im Sage-Assistenten / Bugs with the Sage wizard


ree

Seit geraumer Zeit gibt es einen gravierenden Bug im Sage AppDesigner-Assistenten zur Aktivierung einer Schreibenden Liste auf einer Liste.

In sehr umfangreichen Kundendatenbanken mit vielen Tabellen dauert es entweder extrem lange, bis das Auswahlfeld/die Auswahlliste der Tabelle vervollständigt ist, oder die Auswahlliste wird gar nicht aufgebaut. In beiden Fällen ist kein vernünftiges Arbeiten mit dem Assistenten möglich.😒👎

In diesen Fällen muss man die "Schreibende Liste" manuell aktivieren/erstellen, wie nachfolgend erklärt.


There has been a serious bug in the Sage AppDesigner wizard for activating a "Schreibnde Liste" for some time.

In very extensive customer databases with many tables, it either takes an extremely long time until the selection field/selection list of the table is complete, or the selection list is not built up at all. In both cases, it is not possible to work properly with the wizard.😒👎

In these cases, you have to activate/create the "Schreibende Liste" manually, as explained below.



Manuelles Erstellen der "Schreibenden Liste" / How to build the list manually


  • Dazu beginnt man idealerweise mit dem Datensatz, der auf die SQL-Tabelle verweist, die beschrieben werden soll. Für einen Datensatz ist es dabei nicht notwendig , das Feld "Mandant" explizit hinzuzufügen oder mit einem Wert vorzubelegen - dies übernimmt das System automatisch!

  • Dann erstellt man eine zugehörige Datenquelle für die Liste. Dabei ist ein Filter auf das "Mandant"-Datenfeld zu berücksichtigen.

  • Dann die Liste mit dem Standard Listen-Assistenten erstellen und an die Datenquelle binden.

  • Nun das "edi"-Element mit Bindung an den Datensatz erstellen und als Navigations-Element (Navigation Part References) die Liste auswählen!

  • Den Editiermodus (Navigation Part Edit Mode) nach Bedarf einstellen, z.B. "Editierbar ohne Detailbereich".

  • Nicht vergessen, die Primärschlüsselzuordnung (Felder Schlüsselzuordnung 1-9) korrekt einzustellen, i.d.R. trägt man im Feld "Schlüsselzuordnung 1" einfach die Zahl "1" ein, wenn mit sinnvollen/effizienten IDENTITY-Primärschlüsseln gearbeitet wird (siehe nächster Abschnitt "Ungünstige Primärschlüssel".

  • Zum Schluss erstellt man ein Kontextmenü (Function Set) , ordnet dieses dem "edi"-Element zu , und legt Einträge für das Anlegen und Löschen der Datensätze an (jeweils mit Verweis auf Sage-Standard-Systemfunktionen wie "DatensatzNeu.Sage.System", "DatensatzLoeschen.Sage.System" etc.).

  • Wie bei allen AppDesigner-Elementen ist darauf zu achten, dass die Berechtigungen korrekt gesetzt sind, insbesondere auf den UX/UI-Elementen.


  • Ideally, you start with the record that refers to the SQL table that is to be writable. For a data record, it is not necessary to explicitly add the "Mandant" field or to preassign it a value – the system handles this automatically!

  • Then you create an associated data source for the list. For the list data source you must consider to filter the "Mandant" field.

  • Then use the default list wizard to create the list and bind it to the data source.

  • Now create the "edi" element with binding to the data record and select the list as the navigation element (in "Navigation Part References").

  • Set the editing mode as required, e.g. "Editierbar ohne Detailbereich" (in "Navigation Part Edit Mode").

  • Do not forget to set the primary key assignment (key assignment fields 1-9) correctly, usually simply enter the number "1" in the "Key assignment 1" field if you are working with sensible/efficient IDENTITY primary keys (see next section "Unfavorable primary keys".

  • Finally, you create a context menu, assign it to the "edi" element as a "Function Set" and create entries for creating and deleting the data records (each with reference to Sage standard system functions such as "DatensatzNeu.Sage.System", "DatensatzLoeschen. Sage.System" etc.).

  • As with all AppDesigner elements, care must be taken to ensure that the permissions are set correctly, especially on the UX/UI elements.



Ungünstige Primärschlüssel / Adverse primary keys


Grundsätzlich empfiehlt es sich, eine Primärschlüssel-Situation zu vermeiden, die Datenfelder enthält, die der Anwender bei Datensatz-Updates ändern können darf.

Beispielsweise ist nachfolgende Primärschlüssel-Situation eher ungünstig.


In principle, it is advisable to avoid a primary key situation that contains data fields that the user can change when data records are updated.

For example, the following primary key situation is rather adverse.


ree









Felder wie "Objektart" und "Vorgangsart" und "Position" muss der Anwender ändern dürfen!

Wenn für die "Schreibenden Liste" die Primärschlüssel jedoch änderbar sind, dann führt dies beim Speichern im Bearbeitungsfenster zu einem neuen Datensatz.

Das ist aus Primärschlüssel-Sicht auch nachvollziehbar.

D.h. Sage behandelt diese Situation der Datensatz-Erstellung nicht gesondert und löscht nicht den alten Datensatz.

Es entsteht definitiv ein neuer Datensatz!

Aus Anwendersicht ist dieses Verhalten des Bearbeitungsfensters mehr als verwirrend und unnötig kompliziert.


The user must be able to change fields such as "Objektart", "Vorgangsart", and "Position"!

However, if the primary keys for the "Schreibende Liste" can be changed, this leads to a new data record when saved in the editing window.

This is understandable from the primary key`s point of view.

This means that Sage does not treat this situation of creating a new record separately and does not delete the old data record.

A new data record is definitely being created!

From the user's point of view, this behaviour of the editing window is more than confusing and unnecessarily complicated.



Empfohlener Primärschlüssel / Recommended primary key


Es sollte ein einfacher, interner Primärschlüssel als Datenfeld gewählt werden, z.B. ein "IDENTITY"-Feld (beste Performance) oder Schlüsselverwaltung mit einer Sage TAN.


A simple, internal primary key should be selected as the data field, e.g. an "IDENTITY" field (best performance) or key administration via a Sage TAN.


ree









Im vorliegenden Kundenfall war der Umbau der SQL-Tabelle reicht einfach machbar.

In the customer's case, the conversion of the SQL table was simply feasible.


-- Drop old primary key:
IF EXISTS (SELECT TOP 1 [name] FROM sys.key_constraints WITH(Readuncommitted) WHERE type = 'PK' AND OBJECT_NAME(parent_object_id) = N'BSCrmObjekteVerteilungEintragsarten')
BEGIN
 
  ALTER TABLE [dbo].[BSCrmObjekteVerteilungEintragsarten] DROP CONSTRAINT PK_BSCrmObjekteVerteilungEintragsarten  
 
END
 
GO
 
-- Create new identity field for primary key:
IF EXISTS ( SELECT TOP 1 [Name] FROM dbo.sysobjects WITH(Readuncommitted)
            WHERE id = OBJECT_ID(N'[dbo].[BSCrmObjekteVerteilungEintragsarten]')
            AND OBJECTPROPERTY(id, N'IsUserTable') = 1 )
BEGIN
 
  IF NOT EXISTS ( SELECT TOP 1 [Name] FROM dbo.syscolumns
                  WHERE [Name] = N'IDObjVerteilung'
                  AND id = object_id(N'[dbo].[BSCrmObjekteVerteilungEintragsarten]')
                  AND OBJECTPROPERTY(id, N'IsUserTable') = 1 )
  BEGIN
 
    ALTER TABLE [dbo].[BSCrmObjekteVerteilungEintragsarten] WITH NOCHECK ADD
      [IDObjVerteilung] [INT] NOT NULL IDENTITY(1,1)
 
  END
 
END
 
GO
 
-- Create new primary key:
IF NOT EXISTS (SELECT TOP 1 [name] FROM sys.key_constraints WITH(Readuncommitted) WHERE type = 'PK' AND OBJECT_NAME(parent_object_id) = N'BSCrmObjekteVerteilungEintragsarten')
BEGIN
 
  ALTER TABLE [dbo].[BSCrmObjekteVerteilungEintragsarten] ADD CONSTRAINT [PK_BSCrmObjekteVerteilungEintragsarten]
    PRIMARY KEY CLUSTERED
  (
    [IDObjVerteilung]
  ) 
  ON [PRIMARY]
 
END
 
GO

Die durch den ursprünglich zusammengesetzten Primärschlüssel angedachte Datenlogik kann sehr leicht mit einem Trigger erreicht werden. Siehe nachfolgendes Trigger-Beispiel.


The data logic intended by the original composite primary key can be achieved very easily with a trigger. See the trigger example below.


IF  EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[TRIU_SAG_BSCrmObjekteVerteilungEintragsarten]'))
BEGIN
 
  DROP TRIGGER [dbo].[TRIU_SAG_BSCrmObjekteVerteilungEintragsarten]
 
END
 
GO
 
CREATE TRIGGER [dbo].[TRIU_SAG_BSCrmObjekteVerteilungEintragsarten]
ON [dbo].[BSCrmObjekteVerteilungEintragsarten] FOR INSERT, UPDATE
AS
 
  SET NOCOUNT ON
   
  IF UPDATE([Mandant])
  OR UPDATE([Objektart])
  OR UPDATE([Vorgangsart])
  BEGIN
     
    IF EXISTS ( select TOP 1 SAGChk.[IDObjVerteilung] from [dbo].[BSCrmObjekteVerteilungEintragsarten] AS SAGChk WITH(Readuncommitted)
                INNER JOIN inserted ON SAGChk.[IDObjVerteilung] <> inserted.[IDObjVerteilung]
                AND ISNULL(SAGChk.[Mandant], 0) = ISNULL(inserted.[Mandant], 0)
                AND LTRIM(RTRIM(ISNULL(SAGChk.[Objektart], N''))) = LTRIM(RTRIM(ISNULL(inserted.[Objektart], N'')))
                AND LTRIM(RTRIM(ISNULL(SAGChk.[Vorgangsart], N''))) = LTRIM(RTRIM(ISNULL(inserted.[Vorgangsart], N'')))
              )
    BEGIN
      Raiserror('SAG: 202108301: Es gibt bereits einen Datensatz mit derselben Daten-/Feldinhalt-Kombination! Die Kombination aus "Objektart" und "Vorgangsart" muss eindeutig sein!',16,-1)
      ROLLBACK TRANSACTION
      RETURN
    END
   
  END
 
GO

Primärschlüssel-Felder nicht schreibschützen / Do not set primary key fields to read-only


Beim Einsatz einer "Schreibenden Liste" mit Primärschlüsseln führt ein "Schreibschutz" auf Primärschlüssel-Feldern zu Fehlern bei der Handhabung der Datensätze beim Insert/Anlegen neuer Datensätze.

Siehe Einstellungen beim Datensatz nachfolgend.


When using a "Schreibende Liste" with primary keys, any write protection on primary key fields leads to errors in handling the data records when inserting/creating new data records. See settings for data set below.


ree

Auf keinen Fall darf bei "Schreibenden Listen" das Attribut/die Eigenschaft "Schreibschutz?" aktiviert werden!

Ansonsten kommt beim Speichern in der UX/UI-Oberfläche beim Anlegen eines neuen Datensatzes folgender Fehler im Sage Tracelog.


With "Schreibende Liste", under no circumstances the attribute/property "Schreibschutz?" (Write protection) should be activated!

Otherwise, when saving in the UX/UI interface when creating a new data record, the following error appears in the Sage Tracelog.


ree





















DataRecordHandler/Create: Sagede.Core.Data.DataActionResultException: Exception of type 'Sagede.Core.Data.DataActionResultException' was thrown. at Sagede.Shared.RealTimeData.Core.Handlers.DataRecordHandler.CreateInternal(DataRecord definition, DataContainer data, Boolean updateIfExist, DataRecordOperationContext operationContext, ...

Daraus folgt, dass man diese Primärschlüsselfelder nicht mit o.g. Eigenschaft in der UX/UI einbauen kann, da der Anwender ansonsten den Eindruck hat, er könne diese ändern.

Man muss das Feld dann über ein Makro überall auf "Nur lesen" setzen.

Auf dem "edi"-Element z.B., wie nachfolgend zu sehen.


This means that these primary key fields cannot be put in the UX/UI with the above-mentioned property; otherwise, the user would have the impression that he could change them.

Using a macro, You have to set the field to "Read only" everywhere.

For example, on the "edi" element, as shown below.



ree




⚠️Das Vorbelegen im Ereignis "Beim Anzeigen" (On Current) funktioniert nicht (immer) korrekt , da dies in diesem Kontext ausschließlich beim Datensatz-Wechsel ausgeführt wird! Weiterhin sollte man auf die Abfrage des UX/UI-Kontext mit "Not IsRunningOnServer()" an dieser Stelle verzichten und die Makrozeilen immer ausführen lassen!⚠️


Mit "FestlegenFeldAktiv" bzw. "SetFieldReadOnly" kann das Feld einfach in der Maske für direkte Eingaben gesperrt werden - es empfiehlt sich (siehe Screenshot) das "Beim Öffnen" (On Open)-Ereignis dafür zu verwenden - das "Beim Anzeigen" (On Current)-Ereignis wird in diesem Kontext nur beim Datensatzwechsel ausgelöst
Mit "FestlegenFeldAktiv" bzw. "SetFieldReadOnly" kann das Feld einfach in der Maske für direkte Eingaben gesperrt werden - es empfiehlt sich (siehe Screenshot) das "Beim Öffnen" (On Open)-Ereignis dafür zu verwenden - das "Beim Anzeigen" (On Current)-Ereignis wird in diesem Kontext nur beim Datensatzwechsel ausgelöst

Bei "Identity"-Datenfeldern ist es wichtig , den "Primärschlüsseltyp" auf "Automatisch hochgezählt" (5) zu setzen.

For "Identity" data fields it is important to set the "Primärschlüsseltyp" (Primary key type) to "Automatisch hochgezählt" (5), which is Auto increment.

ree














Performance

Aus Performance-Gründen machen Datenreferenzen in Datenquellen, die an Listen oder andere UX/UI-Elemente direkt gebunden sind, die Massendaten anzeigen, oft keinen Sinn.

Es kann performanter sein, Matchcodes und Klartext-Informationen zu Schlüsseln über eine View oder Unterabfragen bereitzustellen.


For performance reasons, data references in data sources that are directly bound to lists or other UX/UI elements that display bulk data often make no sense.

It can be more efficient to provide matchcodes and plain text information about keys via a view or subqueries.






bottom of page