Tipps, Tricks & Beispiele
Auf dieser Seite finden Sie praxisnahe Codebeispiele und Lösungen zu häufigen Aufgaben mit der COM-Aktiv-Schnittstelle. Die Beispiele stammen aus dem laufenden Support-Betrieb und werden fortlaufend ergänzt.
Ergänzende Referenz
Die grundlegenden Konzepte zu Verbindung, Ranges und Filtern werden ausführlich unter Verbindung und Datenzugriff beschrieben. Die hier gezeigten Beispiele dienen als konkrete Ergänzung.
Regeln bei COM-Zugriffen
Die microtech Software verfügt über ein Regelsystem, das bei bestimmten Aktionen automatisch greift. Nicht alle Regeln werden bei Zugriffen über die COM-Schnittstelle ausgelöst. Die folgende Übersicht zeigt, welche Auswertungspositionen bei COM-Zugriffen beachtet werden und welche nicht.
Positionen
Beachtet bei COM-Zugriffen:
- Vor dem Speichern nach einer Neuanlage oder Änderung (über das Erfassungsformular)
- Für das Bearbeiten von Positionen bzw. nach dem Wandeln / Einladen (über das Erfassungsformular) von Positionen (im Zielvorgang)
- „Vor dem Übernehmen von Vorgangspositionen"
Nicht beachtet bei COM-Zugriffen:
- Während der Erfassung
- Positions-Prüfung vor dem Speichern Lauf 1 (über das Erfassungsformular)
- Positions-Prüfung vor dem Speichern Lauf 2 (über das Erfassungsformular)
Vorgänge
Beachtet bei COM-Zugriffen:
- Vor dem Buchen von Vorgängen
- Nach dem Buchen / Stornieren von Vorgängen
- Vor dem Speichern (nach einer Neuanlage, Änderung oder einem Import)
- Während der Erfassung
Nicht beachtet bei COM-Zugriffen:
- Vor dem Speichern nach einer Neuanlage oder Änderung (über das Erfassungsformular)
Wandeln
Beachtet bei COM-Zugriffen:
- Nach dem Wandeln / Einladen (über das Erfassungsformular)
- Für das Bearbeiten von Positionen bzw. nach dem Wandeln / Einladen (über das Erfassungsformular) von Positionen (im Zielvorgang)
- Nach dem Übernehmen von Vorgangspositionen (im Zielvorgang)
- Vor dem Übernehmen von Vorgangspositionen
Nicht beachtet bei COM-Zugriffen:
- Nach dem Übernehmen von Vorgangspositionen (im Quellvorgang)
Adressen
Beachtet bei COM-Zugriffen:
- Vor dem Speichern (nach einer Neuanlage, Änderung oder einem Import)
Nicht beachtet bei COM-Zugriffen:
- Alle anderen Auswertungspositionen
Artikel
Nicht beachtet bei COM-Zugriffen:
- Vor dem Speichern (nach einer Neuanlage, Änderung oder einem Import)
- Vor dem Speichern nach einer Neuanlage oder Änderung (über das Erfassungsformular)
- Beim Buchen / Stornieren von Positionen (aus Vorgängen)
Wahl der Vorgangspositionen beim Wandeln
Eine direkte Möglichkeit, beim Wandeln per COM einzelne Positionen an- oder abzuwählen, besteht nicht. Im SpecialObject AutoWandeln kann definiert werden, ob die Liefermenge beachtet werden soll und ob diese vor dem Wandeln neu berechnet wird.
Eine Position, deren Neuberechnung eine Liefermenge von 0 ergibt, oder eine Position, die bereits vor dem Wandeln die Liefermenge 0 hat, wird bei aktivierter Option „Liefermenge beachten" nicht mitgewandelt. Über die Liefermenge kann also gesteuert werden, welche Positionen gewandelt werden.
Sollen bestimmte Positionen unabhängig von der Liefermenge vom Wandeln ausgeschlossen werden, kann eine Positions-Regel „Vor dem Übernehmen von Vorgangspositionen" angelegt werden. Die Bedingung wird frei gestaltet; als Regelanweisung wird für das Feld WandelnKz der Feldwert gelöscht.
Grundlagen der COM-Verbindung
Init und SelectMand
Die Aufrufe Init und SelectMand sind die ersten Funktionen, die bei jeder COM-Anbindung Verwendung finden.
Verbindungsaufbau (Pseudocode)
BpApp.Init("Ihr Firmenname", "", "Ihr Anmeldename", "Ihr Password")
BpApp.SelectMand("Ihre Mandantennummer")
Der Parameter „Ihr Firmenname" entspricht dem Firmennamen, der in der microtech Software unter DATEI > HILFE > SUPPORT & HOTLINE angezeigt wird. Im dort geöffneten Fenster finden Sie unter „Ihre Kundendaten bei microtech" den Firmennamen und die Postleitzahl. Achten Sie auf exakte Groß-/Kleinschreibung und enthaltene Leerzeichen.
Beim Aufruf von SelectMand() wird die Nummer des gewünschten Mandanten als String übergeben. Die Vorgabe ist numerisch, es sind jedoch auch alphanumerische Werte möglich.
Voraussetzungen:
- Der Mandant muss COM-Zugriffe erlauben: DATEI > INFORMATIONEN > AKTUELLE FIRMA / FILIALE / MANDANT > MANDANT BEARBEITEN, Karteiseite „weitere Angaben" → Option „COM Schnittstelle für registrierte Anwendungen verfügbar" aktivieren.
- Der verwendete Benutzer benötigt das Recht „Fremdanwendungen dürfen über registrierte COM-Schnittstelle auf büro+ Daten zugreifen (und bearbeiten)" unter DATEI > INFORMATIONEN > EINSTELLUNGEN > Berechtigungsstrukturen > GLOBAL > DATEI.
- Die Programminstallation muss im normalen Modus (auch nach jedem Update) mindestens einmal gestartet worden sein.
Bei Fehlermeldungen: Aktivieren Sie die erweiterte Fehlerprotokollierung. In vielen Fällen wird dann eine detailliertere Meldung über die COM-Schnittstelle ausgegeben.
COM-Aktiv Developer/Solution Partner: Bei einer Developer/Solution-Partner-Anbindung ändert sich der Init-Aufruf:
Init mit Connection Key
Init("Connection String", "Connection Key", "Anmeldename", "Password")
Connection String und Connection Key werden von microtech bereitgestellt. Vom Endbenutzer müssen nur noch Anmeldename und Passwort abgefragt werden.
Namen und Datentypen von Datenbankfeldern
Um eine Liste aller Datenbanken, Datenfelder und Indices zu erhalten, nutzen Sie das Feldliste-Werkzeug. Eine Anleitung dazu finden Sie unter Verbindung und Datenzugriff.
Die erzeugte Datei FELD.LST enthält alle Felder, mit denen Sie arbeiten können. Der * kennzeichnet Calculated Fields - diese sind nicht immer per Zuweisung änderbar. Anhand des Datentyps sehen Sie, wie die Felder weiter verarbeitet werden müssen (z. B. Array → BetragGruppe, Info → RichEdit).
Ein Feld mit dem Datentyp Table ist ein Verweis auf eine andere Tabelle. Über Feld.Info kann der Name der verknüpften Tabelle ermittelt werden. Meist wird zur Verknüpfung der Primärschlüssel der zugrundeliegenden Tabelle verwendet - das Feld selbst enthält keinen Wert.
Ausbaustufen
Nicht alle Felder sind in allen Ausbaustufen verfügbar. Es kann nur auf Felder und Tabellen zugegriffen werden, die in der zugrundeliegenden Version vorhanden sind.
Schreibschutz einzelner Felder kann sich ändern
Auch ob ein einzelnes Feld über COM beschreibbar ist, ist nicht dauerhaft festgelegt - ein Beispiel ist das Feld DBKuNa der Tabelle Regeln, das seit Programmgeneration 24 beschreibbar ist. Details dazu und ein generelles Vorgehen bei ähnlichen Fällen finden Sie unter Verbindung und Datenzugriff, Abschnitt "Felder lesen und schreiben".
Exception Handling und Transaktionen
Folgende Grundregeln gelten für alle Datenbankzugriffe:
- Alle Datenbankzugriffe mit einer Exception-Behandlung versehen.
- Jeder Fehler nach einem
EditoderAppendund vor dem zugehörigenPostmuss einCancelauslösen. - Jede Transaktion muss eine Fehlerbehandlung haben. Ein
Rollbackmuss in jedem Fehlerfall gewährleistet sein. LockTabledurch intelligentes Retry-Management vermeiden.
Exception Handling für Tabellen (Delphi)
DataSet.Edit;
try
//... DataSet bearbeiten ...
DataSet.Post;
except
DataSet.Cancel;
raise;
end;
Exception Handling für Transaktionen (Delphi)
DataSet.StartTransaction;
try
//... Aktionen in Tabelle A ausführen ...
//... Aktionen in Tabelle B ausführen ...
//... Aktionen in Tabelle n ausführen ...
DataSet.Commit;
except
DataSet.Rollback;
raise;
end;
Erweitertes Beispiel für Transaktionen und NestedDataSets (Delphi)
var
bTransactionStarted := boolean;
bTransactionStarted := DataSet.TryStartTransaction;
try
DataSet.First;
while not DataSet.EOF do
begin
DataSet.Edit;
try
DataSet.Fld['bspBoolean'].AsBoolean := False;
DataSet.Post;
except
DataSet.Cancel;
raise
end;
DataSet.Next;
end;
// Blob Table Elemente
DataSet2.Edit;
try
NestedDataSet := DataSet2.NestedDataSets['Nested'];
try
NestedDataSet.Last;
while not NestedDataSet.BOF do
begin
NestedDataSet.Edit;
try
NestedDataSet.Fld['BspBoolean'].AsBoolean := False;
NestedDataSet.Post;
except
NestedDataSet.Cancel;
raise
end;
NestedDataSet.Prior;
end;
NestedDataSet.PostNestedDataSet;
finally
NestedDataSet := nil;
end;
DataSet2.Post;
except
DataSet2.Cancel;
raise;
end;
if bTransactionStarted then
DataSet.Commit;
except
if bTransactionStarted then
DataSet.RollBack
else
DataSet.TransactionCorrupted;
raise;
end;
Exception Handling (Visual Basic)
Public Sub Demo()
Call DataSet.Append
On Error GoTo PostError
' ...DataSet bearbeiten...
DataSet.Post 'speichern
GoTo Clear
PostError:
DataSet.Cancel
Resume Next
Clear:
Set DataSet = Nothing
End Sub
Die Aufrufe einer Transaction stehen mit den Optionen RequestTimeOut und RequestRetryCount in Verbindung. Damit kann der Programmablauf kontrolliert werden:
Retry-Management (Pseudocode)
Application.RequestTimeOut = 2000 // 2 Sekunden für die Aktion
Application.RequestRetryCount = 0 // Beim ersten Fehlschlag Exception auslösen
while TransactionFailed oder Retry < 10 do
TransactionFailed = DoTransaction
Retry = Retry + 1
end while
Type Library einsehen
Um nachzusehen, welche Elemente in der Type Library existieren, verwenden Sie einen beliebigen Type Library Viewer, der mit eingebetteten TLBs umgehen kann. Die BpNexT.exe im Installationspfad (\Buero Plus NexT) enthält die TLB-Beschreibung mit der kompletten Definition aller Funktionsaufrufe und Aufzählungstypen.
Alternativ können Sie z. B. Microsoft Word verwenden: Öffnen Sie den Visual-Basic-Editor unter Extras > Makro > Visual Basic-Editor, setzen Sie einen Verweis auf die „BpNT Bibliothek" unter Extras > Verweise, und öffnen Sie den Objektkatalog unter Ansicht > Objektkatalog.
Weiterführende Informationen
Für grundlegende Fragen zur COM-Programmierung verwenden Sie die Dokumentation Ihrer Programmiersprache. Relevante Suchbegriffe: Verweise, Type Library, Automatisierung, COM, Interface, CreateOleObject, Early Binding, Late Binding, Gültigkeitsdauer, Reference Count, Component Object Model, QueryInterface.
BpRefLinks
BpRefLinks ermöglichen die Navigation innerhalb der microtech Software über ein URL-artiges Protokoll. Sie können z. B. in Schaubildern verwendet werden.
Aufbau
BpRef://<Bezeichner>@|<ParameterListe>
Der Bezeichner kann frei gewählt werden und dient der Identifikation des Links. Er kann auch leer sein. Die Parameterliste enthält mehrere Parameter im Schema <Name>=<Wert>, getrennt durch Komma.
Aktionen
hacOnline - Öffnet eine Webseite im Standardbrowser:
Webseite öffnen
BpRef://Microtech Webseite öffnen@|Action=hacOnline,Adresse=microtech.de
hacGoto - Führt programminterne Funktionen aus. Der Bereich wird über den Parameter DbsKz festgelegt:
In einen Bereich navigieren
BpRef://Alle_Vorgänge@|Action=hacGoto,DbsKz=dbsVorgang
BpRef://Stammdaten Vertreter@|Action=hacGoto,DbsKz=dbsVertreter
BpRef://Bestellvorschlag@|Action=hacGoto,DbsKz=dbsBestellvorschlag
Die Werte für DbsKz können im entsprechenden Bereich eingesehen werden: In der Übersicht steht unter Hilfe > Erzeuge Navigationslink ein Eintrag bereit, der einen fertigen Navigationslink erzeugt.
Suche und Index
Mit den Parametern Index und Suche wird gesteuert, was im Bereich angezeigt oder gesucht wird:
Suche mit Index
bpref://Vertreter aus Bad Kreuznach@|Action=hacGoto,DbsKz=dbsVertreter,Index=Ort,Suche=Bad Kreuznach
bpref://Interessenten@|Action=hacGoto,DbsKz=dbsAdressen,Index=Status,Suche=Interessent
Vorauswahl mit ExtSuche
In Bereichen mit Vorauswahl muss zusätzlich ExtSuche angegeben werden:
ExtSuche-Beispiele
bpref://Lastschriften@|Action=hacGoto,DbsKz=dbsZahlungsVerkehr,ExtSuche=7
bpref://Lieferscheine Anzeigen@|Action=hacGoto,DbsKz=dbsVorgang,ExtSuche=50
bpref://Eine Rechnung suchen@|Action=hacGoto,DbsKz=dbsVorgang,ExtSuche=70,Index=BelegNr,Suche=RE1234
Funktionskennzeichen (FktKz)
Mit FktKz wird gesteuert, welche Aktion ausgeführt wird. Für jeden Bereich stehen dieselben Kennzeichen zur Verfügung: fktNeu (Neuanlage), fktDrucken (Druckübersicht), fktAendern (Datensatz ändern), fktKopieren (Kopieren), fktLoeschen (Löschen).
Aktionen per FktKz
BpRef://Adressen Drucken@|Action=hacGoto,DbsKz=dbsAdressen,FktKz=fktDrucken
BpRef://Artikel_erfassen@|Action=hacGoto,DbsKz=dbsArtikel,FktKz=fktNeu
BpRef://Vertreter aus BK kopieren@|Action=hacGoto,DbsKz=dbsVertreter,Index=Ort,Suche=Bad Kreuznach,FktKz=fktKopieren
SendKeys und InfoText
Über SendKeys können Tastaturbefehle mitgegeben werden. Die Befehle müssen in doppelte Anführungszeichen gesetzt werden:
SendKeys-Beispiele
bpref://Fibu_Buchungen_einlesen@|Action=hacGoto,DbsKz=dbsBuchungenFiBu,SendKeys="[Alt+P] [L]"
bpref://Zahlungseingang_aktualisieren@|Action=hacGoto,DbsKz=dbsBankKontoAuszuege,SendKeys="[Alt+O]"
bpref://Geschaeftsvorfaelle neue Buchung@|Action=hacGoto,DbsKz=dbsBuchungenFiBu,SendKeys="[Alt+N]"
Mit InfoText kann ein Text mitgegeben werden, der dem Benutzer angezeigt wird:
InfoText-Beispiel
bpref://Mitarbeiter_infoText@|Action=hacGoto,DbsKz=dbsMitarbeiter,InfoText=Wählen Sie einen Mitarbeiter aus und prüfen Sie seine Abrechnung.
Die Berechtigungsprüfung ist voll integriert - ein Benutzer ohne Berechtigung für einen Datenbereich gelangt auch nicht über einen BpRefLink dorthin. Das BpRef-Protokoll ist systemweit registriert und kann von überall aus ausgeführt werden.
Mandanten auflisten
Mandanten abfragen (Delphi)
Program delphi;
var
BpApp: Variant;
BpTbl: Variant;
begin
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Anmeldedaten','','Benutzer','Passwort');
BpTbl := BpApp.MandList;
ShowMessage(IntToStr(BpTbl.RecordCount));
BpTbl.First;
ShowMessage(BpTbl.Fields['MandNr'].AsString);
VarClear(BpTbl);
VarClear(BpApp);
end;
Kundendaten auslesen
Die Funktion GetKundendaten liefert eine eindeutige Identifizierung des Kunden bei microtech. Sie kann mit dem Login GetKundendaten aufgerufen werden, ohne dass ein Mandant ausgewählt sein muss.
Kundendaten abfragen (Delphi)
procedure Kundendaten;
var
KdNr, FrmN, Plz: Variant;
begin
try
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Ihr Anbindungsname', 'Ihr Key', 'Ihr Login', 'Ihr Passwort');
BpApp.GetKundendaten(KdNr, FrmN, Plz);
Showmessage(KdNr + ' ' + FrmN + ' ' + Plz);
finally
BpApp := nil;
end;
end;
Aufruf ohne reguläre Anmeldung
BpApp.Init('GetKundendaten','','','');
Versionsnummer
Das aktuelle Versionsnummernformat (Stand 2026) hängt davon ab, ob es sich um die Release- oder BETA-Version handelt und ob "GetVersion" oder "GetVersionEx" verwendet wird:
| Format | GetVersion | GetVersionEx |
|---|---|---|
| Release | 26.0.7200 | Enterprise-26.2.7200 |
| BETA | 26.0.7204.63 | Enterprise-26.2.7204.63 |
Die Bestandteile: Hauptversion (jährlich geändert), Nebenversion (Quartal, in dem die Version veröffentlicht wurde), Build-Version, Build-Ergänzung bei BETA-Version. Bei eigenen Prüfungen müssen alle 4 Blöcke berücksichtigt werden.
Versionsnummer über COM abfragen
GetVersion und GetVersionEx (Delphi)
procedure Version;
begin
try
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Ihr Anbindungsname', 'Ihr Key', 'Ihr Login', 'Ihr Passwort');
Showmessage(BpApp.GetVersion);
ShowMessage(BpApp.GetVersionEx);
finally
BpApp := nil;
end;
end;
Die Funktion kann auch mit dem Login GetKundendaten aufgerufen werden.
Versionsnummer ohne COM-Instanz
Die BpNexT.exe trägt eine Versionsinformation, die über die Standard-Windows-Funktionen GetFileVersionInfoSize / GetFileVersionInfo abrufbar ist. Der Speicherort der Datei kann in der Registry unter dem Pfad HKEY_LOCAL_MACHINE\SOFTWARE\microtech\Buero Plus\NexT im Eintrag ProgrammPfad ermittelt werden.
Hinweis
Die auf diesem Weg gelesene Versionierung enthält keine Information über einen eingetragenen Schlüssel. Die schlüsselbasierten Informationen lesen Sie über die COM-Schnittstelle selbst aus.
Konfigurationsdaten speichern und lesen
Die COM-Schnittstelle bietet drei Ebenen zum Speichern von Konfigurationsdaten:
| Ebene | Property | Gültigkeitsbereich |
|---|---|---|
| Mandant | Konfiguration | Mandantenbezogen |
| Benutzer | BzrKonfiguration | Benutzerbezogen |
| Global | GloKonfiguration | Gesamter Datenbestand |
Konfiguration schreiben und lesen (Delphi)
// Schreiben
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Firmenname','','Anmeldename','Password');
BpApp.SelectMand('1');
BpApp.Konfiguration['Wert1'] := 'vollkommen beliebiger String';
BpApp.BzrKonfiguration['Wert2'] := 'vollkommen beliebiger String1';
BpApp.GloKonfiguration['Wert3'] := 'vollkommen beliebiger String2';
VarClear(BpApp);
// Lesen
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Firmenname','','Anmeldename','Password');
BpApp.SelectMand('1');
ShowMessage(BpApp.Konfiguration['Wert1']);
ShowMessage(BpApp.BzrKonfiguration['Wert2']);
ShowMessage(BpApp.GloKonfiguration['Wert3']);
VarClear(BpApp);
Defekte Instanz nachträglich löschen
Wenn eine COM-Instanz sich nicht korrekt beendet hat, kann sie über die Prozess-ID terminiert werden:
Instanz per Prozess-ID beenden (Delphi)
Program Delphi;
const
soAppObject = 4;
PROCESS_TERMINATE = $0001;
var
BpApp: Variant;
Handle: Cardinal;
ProcH: THandle;
ExitCode: Cardinal;
begin
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Ihr Firmenname','','Benutzername','Passwort');
BpApp.writeln('Kontrollfenster');
Handle := (BpApp.GetSpecialObject(soAppObject) as AutoAppObject).GetAppProcessId;
// Hier schwere Arbeit verrichten
BpApp := nil;
// nach der dann die Instanz nicht gelöscht werden kann.
ProcH := OpenProcess(PROCESS_TERMINATE, false, Handle);
TerminateProcess(ProcH, ExitCode);
CloseHandle(ProcH);
end;
Adressen
Adresse und Anschrift anlegen
Eine Adresse und eine Anschrift gehören immer zusammen - eine Anschrift ohne gültige Adresse existiert nicht.
Adresse mit Rechnungs- und Lieferanschrift anlegen (VBA)
' BpApp wird außerhalb der Funktion gesetzt und freigegeben.
Private Sub AdressenAnschriften()
Dim tblAdressen As BpNT.AutoDataSet
Dim tblReAnschriften As BpNT.AutoDataSet
Dim tblLiAnschriften As BpNT.AutoDataSet
Dim lastAdrNr
Set tblAdressen = BpApp.DataSetInfos("Adressen").CreateDataSet
Set tblReAnschriften = BpApp.DataSetInfos("Anschriften").CreateDataSet
Set tblLiAnschriften = BpApp.DataSetInfos("Anschriften").CreateDataSet
On Error Resume Next
' Letzte Adressnummer suchen
tblAdressen.Indices("NrNum").Select
tblAdressen.Last
If IsNumeric(tblAdressen.Fields("AdrNr").AsString) Then
lastAdrNr = CInt(tblAdressen.Fields("AdrNr").AsString) + 1
End If
' Adresse anlegen
tblAdressen.Append
tblAdressen.Fields("AdrNr").AsString = lastAdrNr
tblAdressen.Fields("ReAnsNr").AsInteger = 0
tblAdressen.Fields("LiAnsNr").AsInteger = 1
tblAdressen.Post
' Rechnungsanschrift
tblReAnschriften.Append
tblReAnschriften.Fields("AdrNr").AsString = tblAdressen.Fields("AdrNr").AsString
tblReAnschriften.Fields("AnsNr").AsInteger = 0
tblReAnschriften.Fields("Ort").AsString = "Ort"
tblReAnschriften.Fields("PLZ").AsString = "55545"
tblReAnschriften.Fields("Str").AsString = "Straße"
tblReAnschriften.Fields("Na1").AsString = "Title"
tblReAnschriften.Fields("Na2").AsString = "Vorname"
tblReAnschriften.Fields("Na3").AsString = "Nachname"
tblReAnschriften.Post
' Lieferanschrift
tblLiAnschriften.Append
tblLiAnschriften.Fields("AdrNr").AsString = tblAdressen.Fields("AdrNr").AsString
tblLiAnschriften.Fields("AnsNr").AsInteger = 1
tblLiAnschriften.Fields("Ort").AsString = "Platz"
tblLiAnschriften.Fields("PLZ").AsString = "55546"
tblLiAnschriften.Fields("Str").AsString = "andere Str."
tblLiAnschriften.Fields("Na1").AsString = "Anrede"
tblLiAnschriften.Fields("Na2").AsString = "Li Firma"
tblLiAnschriften.Fields("Na3").AsString = "Firma Zusatz"
tblLiAnschriften.Post
Set tblAdressen = Nothing
Set tblReAnschriften = Nothing
Set tblLiAnschriften = Nothing
End Sub
Tipp
Wenn die Ermittlung der höchsten Adressnummer nicht zeitnah oder nur einmalig durchgeführt wird, sollte eine zusätzliche Prüfung auf bereits existierende Nummern stattfinden. Beachten Sie auch die Option CreateDataSetEx - wenn Sie nur die Standardwerte einer Adresse benötigen, ist deren Verwendung einfacher.
Adresse mit Ansprechpartner anlegen
Ein Ansprechpartner benötigt zur Zuordnung folgende Informationen: AdrNr (Adressnummer), AnsNr (Anschriftennummer), AspNr (laufende Nummer des Ansprechpartners), StdKz (Hauptansprechpartner-Kennzeichen, nur einmal pro AdrNr/AnsNr-Kombination zulässig).
Ansprechpartner für Rechnungsadresse 10000
AdrNr := "10000" ' Adresse des Ansprechpartner-Datensatzes
AnsNr := "0" ' Anschriften-Nummer
AspNr := "0" ' Eigene laufende Nummer
StdKz := TRUE ' Nur einmal pro AdrNr/AnsNr-Kombination
Vollständiges Beispiel mit Ansprechpartner (VBA)
' BpApp wird außerhalb der Funktion gesetzt und freigegeben.
Private Sub AdressenAnschriften()
Dim tblAdressen As BpNT.AutoDataSet
Dim tblReAnschriften As BpNT.AutoDataSet
Dim tblLiAnschriften As BpNT.AutoDataSet
Dim tblAnsprechpartner As BpNT.AutoDataSet
Dim lastAdrNr
Set tblAdressen = BpApp.DataSetInfos("Adressen").CreateDataSet
Set tblReAnschriften = BpApp.DataSetInfos("Anschriften").CreateDataSet
Set tblLiAnschriften = BpApp.DataSetInfos("Anschriften").CreateDataSet
Set tblAnsprechpartner = BpApp.DataSetInfos("Ansprechpartner").CreateDataSet
On Error Resume Next
tblAdressen.Indices("NrNum").Select
tblAdressen.Last
If IsNumeric(tblAdressen.Fields("AdrNr").AsString) Then
lastAdrNr = CInt(tblAdressen.Fields("AdrNr").AsString) + 1
End If
tblAdressen.Append
tblAdressen.Fields("AdrNr").AsString = lastAdrNr
tblAdressen.Fields("ReAnsNr").AsInteger = 0
tblAdressen.Fields("LiAnsNr").AsInteger = 1
tblAdressen.Post
' Rechnungsanschrift
tblReAnschriften.Append
tblReAnschriften.Fields("AdrNr").AsString = tblAdressen.Fields("AdrNr").AsString
tblReAnschriften.Fields("AnsNr").AsInteger = 0
tblReAnschriften.Fields("Ort").AsString = "Ort"
tblReAnschriften.Fields("PLZ").AsString = "55545"
tblReAnschriften.Fields("Str").AsString = "Straße"
tblReAnschriften.Fields("Na1").AsString = "Title"
tblReAnschriften.Fields("Na2").AsString = "Vorname"
tblReAnschriften.Fields("Na3").AsString = "Nachname"
tblReAnschriften.Post
' Lieferanschrift
tblLiAnschriften.Append
tblLiAnschriften.Fields("AdrNr").AsString = tblAdressen.Fields("AdrNr").AsString
tblLiAnschriften.Fields("AnsNr").AsInteger = 1
tblLiAnschriften.Fields("Ort").AsString = "Platz"
tblLiAnschriften.Fields("PLZ").AsString = "55546"
tblLiAnschriften.Fields("Str").AsString = "andere Str."
tblLiAnschriften.Fields("Na1").AsString = "Anrede"
tblLiAnschriften.Fields("Na2").AsString = "Li Firma"
tblLiAnschriften.Fields("Na3").AsString = "Firma Zusatz"
tblLiAnschriften.Post
' Ansprechpartner für Rechnungsanschrift
tblAnsprechpartner.Append
tblAnsprechpartner.Fields("AdrNr").AsString = tblAdressen.Fields("AdrNr").AsString
tblAnsprechpartner.Fields("AnsNr").AsInteger = tblReAnschriften.Fields("AnsNr").AsString
tblAnsprechpartner.Fields("AspNr").AsInteger = 0
tblAnsprechpartner.Fields("StdKz").AsBoolean = TRUE
tblAnsprechpartner.Fields("Anr").AsString = "Frau"
tblAnsprechpartner.Fields("VNa").AsString = "Das"
tblAnsprechpartner.Fields("NNa").AsString = "Beispiel"
tblAnsprechpartner.Post
Set tblAdressen = Nothing
Set tblReAnschriften = Nothing
Set tblLiAnschriften = Nothing
Set tblAnsprechpartner = Nothing
End Sub
Bankverbindungen in Adressen
Beim Hinterlegen von Bankverbindungen ist besonderes Augenmerk auf die Zuweisung via .Text zu legen. Diese Funktionalität erlaubt es, z. B. mit "Überweisung" den internen Wert 9 im Feld ZahlArt zu hinterlegen, ohne den numerischen Wert kennen zu müssen. Diese Funktionalität steht für viele Calculated Fields zur Verfügung.
Bankverbindung hinterlegen (Delphi)
Programm Delphi;
var
BpApp: Application;
BpTbl: AutoDataSet;
BpNestedTbl: AutoDataSet;
begin
BpApp := CoApplication.Create;
BpApp.Init('ConnectionName', 'ConnectionKey', 'UserName', 'UserPass');
BpApp.SelectMand('Mandanten Nummer');
BpTbl := BpApp.DataSetInfos['Adressen'].CreateDataSet;
BpNestedTbl := BpTbl.NestedDataSets['BnkVb'];
// Adresse positionieren
BpTbl.Indices['NR'].Select;
Bptbl.SetKey;
BpTbl.Fields['AdrNr'].AsString := '10000';
BpTbl.GotoNearest;
BpTbl.Edit;
BpNestedTbl.Edit;
BpNestedTbl.Fields['BLZ'].Text := '54500000';
BpNestedTbl.Fields['BLZNr'].Text := '0';
BpNestedTbl.Fields['KNr'].Text := '123456789';
BpNestedTbl.Fields['Inhab'].Text := 'Bank Konto Inhaber';
BpNestedTbl.Fields['IBAN'].Text := 'DE94 5450 0000 0123 4567 89';
BpNestedTbl.Fields['ZahlArt'].Text := 'Überweisung';
// Alternativ: BpNestedTbl.Fields['ZahlArt'].AsInteger := 9;
BpNestedTbl.Post;
BpNestedTbl.PostNestedDataSet;
BpTbl.Post;
end;
Kreditor / Debitor einer Adresse
Ab Build 3922 werden die Buchungskonten zu Adressen anhand der Parameter des Adressstatus automatisch mit angelegt (nur bei Neuanlage).
Adresse mit automatischem Buchungskonto (Delphi)
Tbl := BpApp.DataSetInfos('Adressen').CreateDataSetEx;
Tbl.Append;
try
Tbl.Fields('Status').AsString := 'Hersteller';
Tbl.Fields('AdrNr').AsString := Tbl.SetupNr('');
Tbl.Fields('Re.Na2').AsString := 'ReNa';
Tbl.Post;
except
Tbl.Cancel;
end;
Debitoren-/Kreditorenkonto manuell anlegen (Delphi)
// Kontenplaneintrag anlegen
BpTbl := BpApp.DataSetInfos['Kontenplan'].CreateDataSet;
BpTbl.Append;
BpTbl.Fields['KtoNr'].AsString := '10001';
BpTbl.Fields['Bez'].AsString := 'Eigenes COM Debitoren Konto';
BpTbl.Fields['Kategorie'].Text := 'Personenkonto/Debitoren';
BpTbl.Fields['EBBuchKz'].AsBoolean := True;
BpTbl.Post;
BpTbl := nil;
// Adresse anpassen
BpTbl := BpApp.DataSetInfos['Adressen'].CreateDataSet;
BpTbl.Indices['NR'].Select;
Bptbl.SetKey;
BpTbl.Fields['AdrNr'].AsString := '10001';
BpTbl.GotoNearest;
BpTbl.Edit;
BpTbl.Fields['BKtoNr'].AsString := '10001';
BpTbl.Post;
Tipp
Die Funktion .Text des Datenbankfeldes „Kategorie" erspart das Nachschlagen des numerischen Werts in den Parametern.
Vorgänge
Belegnummer eines neuen Vorgangs auslesen
Alle Felder des Vorgangs sind „live" - also vor dem Post les- und schreibbar. Die Belegnummer kann direkt nach dem Append ausgelesen werden:
Belegnummer auslesen (Delphi)
SpecialObj := BpApp.GetSpecialObject(1);
SpecialObj.Append(30, '10001');
SpecialObj.Positionen.Add(1, 'stk', '8');
Showmessage(SpecialObj.DataSet.Fields['BelegNr'].AsString);
SpecialObj.Post;
Vorgangspositionen
In den Tabellen Vorgang und VorgangArchiv befindet sich ein NestedDataSet VPo - dieses dient jedoch nur der internen Verwaltung und bietet keinen Zugriff auf die zugehörigen Positionen.
Die Vorgangspositionen befinden sich in den Tabellen VorgangPosition und VorgangPositionArchiv. Die zugehörigen Positionen eines Vorgangs können über die Belegnummer per SetRange abgefragt werden.
Infoblätter im Vorgang
Die Inhalte der Infoblätter werden in einem NestedDataSet VIBls gespeichert. Es sind nur für tatsächlich belegte Infoblätter Einträge vorhanden. Der einzig verfügbare Index nutzt das Feld KuBez, und jeder Infoblattwert darf nur einmal vorkommen. Infoblätter müssen in den Parametern definiert sein (BEARBEITEN > PARAMETER > VERKAUF/EINKAUF > VORGÄNGE UND ZWISCHENBELEGE > INFOBLATTBEZEICHNUNGEN).
Infoblätter lesen (Delphi)
Program Delphi;
var
BpApp: Variant;
BpTbl: Variant;
BpTblNested: Variant;
i: Integer;
begin
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Anmeldedaten','','Benutzer','Passwort');
BpApp.SelectMand('1');
BpTbl := BpApp.DataSetInfos('Vorgang').CreateDataSet;
BpTbl.First;
BpTblNested := BpTbl.NestedDataSets['VIBls'];
if BpTblNested.RecordCount = 0 then
Showmessage('Keine Infoblätter belegt');
BpTblNested.First;
for i := 0 to (BpTblNested.RecordCount - 1) do
begin
Showmessage(BpTblNested.Fields['KuBez'].AsString + ' | ' +
BpTblNested.Fields['Text'].AsString);
BpTblNested.Next;
end;
BpTblNested := nil;
BpTbl := nil;
BpApp := nil;
end;
Infoblätter schreiben (Delphi)
Program Delphi;
var
BpApp: Variant;
BpTbl: Variant;
BpTblNested: Variant;
begin
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Anmeldedaten','','Benutzer','Passwort');
BpApp.SelectMand('1');
BpTbl := BpApp.DataSetInfos('Vorgang').CreateDataSet;
BpTbl.First; // Hier den gewünschten Vorgang auswählen
BpTbl.Edit;
BpTblNested := BpTbl.NestedDataSets['VIBls'];
BpTblNested.SetKey;
BpTblNested.Fields['KuBez'].AsString := 'InfoBlt14';
if not BpTblNested.GotoKey then
begin
// Infoblatt nicht vorhanden - neuen Eintrag anlegen
BpTblNested.Append;
BpTblNested.Fields['KuBez'].AsString := 'InfoBlt14';
BpTblNested.Fields['Text'].AsString := 'Beschreibungstext für Info Blatt 14';
end
else
begin
BpTblNested.Edit;
BpTblNested.Fields['Text'].AsString := 'Beschreibungstext für Info Blatt 14';
end;
BpTblNested.Post;
BpTblNested.PostNestedDataSet;
BpTbl.Post;
BpTblNested := nil;
BpTbl := nil;
BpApp := nil;
end;
Artikel / Artikel-Varianten
Artikel-Varianten und -Ausprägungen werden über verschachtelte NestedDataSets verwaltet. Die Varianten befinden sich im NestedDataSet ArtAP der Tabelle Mandant, die zugehörigen Ausprägungen im NestedDataSet ArtAPs innerhalb der Varianten.
Varianten und Ausprägungen auslesen (Delphi)
procedure Auslesen;
var
TblMandant: Variant;
TblVarianten: Variant;
TblAuspraegung: Variant;
begin
TblMandant := BpApp.DataSetInfos['Mandant'].CreateDataSet;
try
if TblMandant.RecordCount < 1 then
exit;
TblVarianten := TblMandant.NestedDataSets['ArtAP'];
try
while not TblVarianten.Eof do
begin
writeln('Variante: ' + TblVarianten.Fields['Nr'].Text + ' - ' +
TblVarianten.Fields['KuBez'].Text + ' - ' +
TblVarianten.Fields['Bez'].Text);
TblAuspraegung := TblVarianten.NestedDataSets['ArtAPs'];
try
if TblAuspraegung.RecordCount < 1 then
exit;
writeln('Ausprägung: ' + TblAuspraegung.Fields['Nr'].Text + ' - ' +
TblAuspraegung.Fields['Bez'].Text);
TblAuspraegung.Next;
finally
TblAuspraegung := nil;
end;
TblVarianten.Next;
end;
finally
TblVarianten := nil;
end;
finally
TblMandant := nil;
end;
end;
Variante mit Ausprägung anlegen (Delphi)
procedure Anlegen;
var
TblMandant: Variant;
TblVarianten: Variant;
TblAuspraegung: Variant;
begin
TblMandant := BpApp.DataSetInfos['Mandant'].CreateDataSet;
try
if not TblMandant.Eof then
begin
TblMandant.Edit;
try
TblVarianten := TblMandant.NestedDataSets['ArtAP'];
try
if not TblVarianten.Eof then
begin
TblVarianten.Append;
try
TblVarianten.Fields['Nr'].Text := '3';
TblVarianten.Fields['KuBez'].Text := 'AAA';
TblVarianten.Fields['Bez'].Text := 'AAA Test';
TblAuspraegung := TblVarianten.NestedDataSets['ArtAPs'];
try
TblAuspraegung.Append;
try
TblAuspraegung.Fields['Nr'].Text := 'Auspraegung';
TblAuspraegung.Fields['Bez'].Text := 'Bezeichnung';
TblAuspraegung.Post;
TblAuspraegung.PostNestedDataSet;
except
TblAuspraegung.Cancel;
end;
finally
TblAuspraegung := nil;
end;
TblVarianten.Post;
TblVarianten.PostNestedDataSet;
except
TblVarianten.Cancel;
end;
end;
finally
TblVarianten := nil;
end;
TblMandant.Post;
except
TblMandant.Cancel;
end;
end;
finally
TblMandant := nil;
end;
end;
Umsätze auslesen
Umsätze gibt es in mehreren Tabellen, z. B. Artikel und Vertreter. Jede dieser Tabellen hat ein NestedDataSet Ums, in dem die Umsätze zum jeweiligen Datensatz abgelegt sind.
Umsätze aus Artikeln lesen (VBA)
Dim BpApp As BpNT.Application
Dim DataSetArtikel As BpNT.IAutoDataSet
Dim NestedUmsatz As BpNT.IAutoDataSet
Set BpApp = CreateObject("BpNT.Application")
Call BpApp.Init("Firmenname", "", "Anmeldename", "Password")
Call BpApp.SelectMand("1")
Set DataSetArtikel = BpApp.DataSetInfos.Item("Artikel").CreateDataSet
Set NestedUmsatz = DataSetArtikel.NestedDataSets("Ums")
For Each Field In NestedUmsatz.Fields
Debug.Print (Field.Name & " " & Field.Value)
Next
' Artikel neu positionieren
DataSetArtikel.Next
For Each Field In NestedUmsatz.Fields
Debug.Print (Field.Name & " " & Field.Value)
Next
Verknüpfte Datentabellen
Um auf die richtigen Datensätze einer verknüpften Tabelle zuzugreifen, muss diese korrekt positioniert werden.
Tipp
Beachten Sie auch die Option CreateDataSetEx - eventuell führt diese schneller zum Ziel.
Adressen und Anschriften verknüpft abfragen (VBA)
Public Sub Adressen_Anschriften()
Dim BpApp As BpNT.Application
Dim DataSetAdressen As BpNT.IAutoDataSet
Dim DataSetAnschriften As BpNT.IAutoDataSet
Set BpApp = CreateObject("BpNT.Application")
Call BpApp.Init("Firmenname", "", "Anmeldename", "Password")
BpApp.SelectMand ("1")
Set DataSetAdressen = BpApp.DataSetInfos.Item("Adressen").CreateDataSet
DataSetAdressen.First
Set DataSetAnschriften = BpApp.DataSetInfos.Item("Anschriften").CreateDataSet
DataSetAnschriften.First
' Index "AdrNrAnsNr" auswählen (KeyField count = 2)
DataSetAnschriften.Indices.Item("AdrNrAnsNr").Select
Do While DataSetAdressen.EOF = False
DataSetAnschriften.EditKey
With DataSetAnschriften.Indices.Item("AdrNrAnsNr").IndexFields
.Item("AdrNr").AsString = DataSetAdressen.Fields.Item("AdrNr").AsString
.Item("AnsNr").AsInteger = DataSetAdressen.Fields.Item("ReAnsNr").AsInteger
End With
If Not DataSetAnschriften.GotoKey Then
MsgBox ("Kein Eintrag gefunden")
End If
DataSetAdressen.Next
Loop
Set DataSetAdressen = Nothing
Set DataSetAnschriften = Nothing
Set BpApp = Nothing
End Sub
Wichtig
Wenn mehrere Felder zu setzen sind, darf die Variable, die auf IndexFields verweist, nur einmal abgeholt werden:
Dim IndexFields
WorkTabel.EditKey
IndexFields = WorkTabel.Indices.Item("ArtNrAdrNr").IndexFields
IndexFields.Item("ArtNr").AsString = "Artikelnummer"
IndexFields.Item("AdrNr").AsString = ""
WorkTabel.GotoKey
Ein leerer Wert bei einem Indexfeld wird so behandelt, als wäre das Feld nicht im Range enthalten.
Datensatzänderungen werden nicht angezeigt
Nach einem COM-Zugriff, der z. B. einen Datensatz als „Gesperrt" kennzeichnet, wird die Änderung in der laufenden microtech Software nicht sofort sichtbar. Eine direkte Funktion zur Aktualisierung der Ansicht gibt es nicht - der Datensatz wird spätestens beim nächsten Zugriff aktualisiert.
Erstellungs- und Änderungsdatum setzen
Die Funktion SetSatzDatum erlaubt das Setzen von Erstellungsdatum, Erstellungsbenutzer, Änderungsdatum und Änderungsbenutzer. Alternativ kann AutoSetSatzDatum verwendet werden - dieser Aufruf muss nach Edit, Append oder Insert erfolgen und setzt automatisch den Zeitpunkt des Postens sowie den Kurznamen des angemeldeten Benutzers.
Unterschied zwischen AendDat und LtzAend:
| Feld | Beschreibung |
|---|---|
LtzAend (Letzte Datensatzänderung) | Wird bei jeder Speicherung aktualisiert, unabhängig davon, was oder wodurch geändert wurde |
AendDat (Änderungsdatum) | Wird nur aktualisiert, wenn SetSatzDatum per COM genutzt wird oder ein Benutzer manuell über die Erfassungsmaske ändert |
.NET in unmanaged Code
In .NET-Sprachen unterscheidet sich die Lebensdauer von COM-Objekten von vergleichbaren Sprachen. Eine Referenz auf ein COM-Objekt wird per Voreinstellung erst beim Beenden des Programms freigegeben. Wenn erneut eine gültige Instanz angefordert werden soll, muss die Freigabe explizit programmiert werden.
COM-Objekte in .NET freigeben
System.GC.Collect(0);
System.Runtime.InteropServices.Marshal.ReleaseComObject(Instanz1);
System.Runtime.InteropServices.Marshal.ReleaseComObject(Instanz2);
Alternative mit null-Zuweisung
System.Runtime.InteropServices.Marshal.ReleaseComObject(Instanz1);
Instanz1 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
IDisposable-Pattern (C#)
public class BpNTWorker : IDisposable
{
BpNT.Application _BpNTApp = null;
BpNT.Anything _Anything = null;
public void Work()
{
_BpNTApp = new ApplicationClass();
// Work with _Application
}
public void Dispose()
{
if (_BpNTApp != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(_BpNTApp);
if (_Anything != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(_Anything);
}
}
Deployment
Die erstellte interop.dll, die COM und .NET verbindet, gehört zum Deployment-Vorgang Ihres Projektes. Bei Änderungen oder Erweiterungen der COM-Schnittstellen-Definition wird dies beim Kompilieren in der DLL abgelegt.
Bankverbindung wird nicht angezeigt
Eine gültige Bankverbindung besteht aus drei Feldern: Blz (Bankleitzahl), KNR (Kontonummer) und BlzNr (Bankleitzahlnummer). Ohne die BlzNr kann die Bankverbindung in den meisten Fällen nicht korrekt angezeigt werden. Der Standardwert für BlzNr ist 0 - dieser Wert wird verwendet, um z. B. Sparkassen mit einer Bankleitzahl und mehreren Filialen zu unterscheiden.
Wird eine Bankleitzahl verwendet, die in der Bankentabelle nicht enthalten ist, schlägt die Anzeige trotz gesetzter BlzNr fehl.
Hinweis
Die Feldnamen können variieren, z. B. durch andere Präfixe.
Gültige Zeichen
Um eine Eingabevalidierung für einzelne Felder zu erstellen, kann die Liste der gültigen Zeichen abgefragt werden:
[Autofeld].FieldInfo.ValidChars
Der Rückgabewert ist ein String mit allen zulässigen Eingabezeichen für dieses Feld.
Performance-Hinweis (ab Build 5000)
Diese Funktion prüft alle Unicode-Zeichen, die ein Feld enthalten könnte, und hat daher eine gewisse Laufzeit. Rufen Sie ValidChars wenn möglich nur einmal pro Feld auf und verwenden Sie das Ergebnis für alle weiteren Zugriffe.
Interface IUnknown - was muss getan werden?
Bei allen Funktionen, die „Stream" im Namen tragen, wird als Standardparameter IUnknown in der Deklaration verwendet. Ein SaveToStream erwartet beispielsweise als Parameter eine Variable, die das IStream-Interface unterstützt.
Layout per IStream importieren (Delphi)
procedure TForm1.Button1Click(Sender: TObject);
var
BPNT: OleVariant;
BPNTLayoutGroup: OleVariant;
ExecLayout: OleVariant;
Layout: OleVariant;
Stream: IStream;
FileStream: TFileStream;
begin
FileStream := TFileStream.Create('C:\Artikel Export.BPL',
fmOpenRead or fmShareDenyWrite);
Stream := (TStreamAdapter.Create(FileStream, soOwned) as IStream);
try
BPNT := CreateOleObject('BpNT.Application');
BpNT.Init('Firmenname','','Anmeldename','Passwort');
BpNT.SelectMand('Mandantennummer');
BPNTLayoutGroup := BpNT.OpenLayoutGroup(
WideString('Au'), WideString('ArtExpListe'));
Layout := BPNTLayoutGroup.ImportLayoutFromStream(
IUnknown(Stream), WideString('Test Name'), WordBool(True));
ExecLayout := Layout.doExport;
ExecLayout.Execute;
finally
Stream := nil;
end;
end;
COM-Instanzen in Services
Beim Einsatz der COM-Schnittstelle aus einem Windows-Dienst (Service) heraus können keine Meldungsfenster ausgegeben werden. Das Programm würde auf eine Eingabe warten und an dieser Stelle stehen bleiben.
Lösungsansätze:
- Bei Ihrem Dienst die Option „Datenaustausch zwischen Dienst und Desktop zulassen" aktivieren (Reiter „Anmelden" in den Diensteigenschaften). Diese Option stellt jedoch eine mögliche Sicherheitsverletzung dar.
- Die Callback-Schnittstelle für Meldungen verwenden (
OnMeldFenster). - Die COM-Instanz einmalig außerhalb des Dienstes starten, um die Meldung zu bestätigen. Beachten Sie, dass zehn Tage vor Ablauf eines Schlüssels immer eine Meldung erscheint.
NestedDataSet - häufiger Fehler
Die Fehlermeldung „Datenmenge weder im Einfüge- noch Editiermodus" tritt auf, wenn das NestedDataSet nicht korrekt verwendet wird.
Falsch
Set VorgangTbl = Vorgang.DataSet
VorgangTbl.NestedDataSets("VIBls").Edit
VorgangTbl.NestedDataSets("VIBls").Fields("Text").AsString = "Testeintrag"
' -> Fehler: "Datenmenge weder im Einfüge- noch Editiermodus"
VorgangTbl.NestedDataSets("VIBls").Post
VorgangTbl.Post
Richtig
Set VorgangTbl = Vorgang.DataSet
VorgangTbl.Edit ' Master-Tabelle in den Edit-Modus versetzen
Set Nested = VorgangTbl.NestedDataSets("VIBls") ' Eigene Variable zuweisen
Nested.Edit
Nested.Fields("Text").AsString = "Testeintrag"
Nested.Post
Nested.PostNestedDataSet ' NestedDataSets in die Master-Tabelle schreiben
VorgangTbl.Post
Drei Regeln für NestedDataSets:
- Die Master-Tabelle muss sich selbst im Edit- oder Insert-Modus befinden.
- Jeder Aufruf von
NestedDataSets("Name")erzeugt ein eigenständiges Objekt - das NestedDataSet daher immer einer eigenen Variablen zuweisen. - Damit Änderungen korrekt gespeichert werden, muss
PostNestedDataSetaufgerufen werden.
Ranges - Beispiele
Grundlagen
Die grundlegende Dokumentation zu Ranges finden Sie unter Verbindung und Datenzugriff. Hier werden ergänzende Beispiele mit besonderem Fokus auf das Verhalten bei mehreren Indexfeldern gezeigt.
Verhalten bei zusammengesetzten Indices
Gegeben sei eine Anschriften-Tabelle mit dem Index AdrNrAnsNr (Felder: Adressnummer als String, Anschriftennummer als Integer):
| Adressnummer | Anschriftennummer | Name |
|---|---|---|
| 10000 | 0 | Anton |
| 10001 | 0 | Bertha |
| 10001 | 1 | Cäsar |
| 10002 | 0 | Dora |
| 10002 | 1 | Emil |
| 10002 | 3 | Friedrich |
| 10010 | 2 | Gustav |
| 10010 | 3 | Heinrich |
Range mit zwei Indexfeldern (Delphi)
tbl := BpApp.DatasetInfos['Anschriften'].CreateDataSet;
tbl.Indices.Item['AdrNrAnsNr'].Select;
tbl.SetRangeStart;
tbl.Fields['AdrNr'].AsString := '10000';
tbl.Fields['AnsNr'].AsInteger := 1;
tbl.SetRangeEnd;
tbl.Fields['AdrNr'].AsString := '10010';
tbl.Fields['AnsNr'].AsInteger := 2;
tbl.ApplyRange;
Aus SQL-Sicht würde man 3 Datensätze erwarten (Cäsar, Emil, Gustav). Tatsächlich liefert der Range 6 Datensätze: Bertha, Cäsar, Dora, Emil, Friedrich und Gustav. Die Erklärung liegt in der Range-Logik:
- Von:
(Adressnummer > 10000)ODER((Adressnummer = 10000) UND (Anschriftennummer >= 1)) - Bis:
(Adressnummer < 10010)ODER((Adressnummer = 10010) UND (Anschriftennummer <= 2))
Werden in einem Range mehrere Indexfelder übergeben, findet nur das erste Feld vollständige Beachtung. Alle weiteren Felder werden nur für die Datensätze ausgewertet, die über das erste Feld als Anfang oder Ende bestimmt wurden.
Gegenmaßnahmen: Das Ergebnis entweder mit einem Filter einschränken oder den Zugriff anders gestalten (z. B. mit KeyFieldCount).
Filter
Jedes DataSet kann über die Eigenschaft Filter bestimmte Datensätze aus der Ergebnismenge ausschließen. Filter können nur Felder verwenden, die in der Datenmenge enthalten und nicht berechnet sind (Calculated Fields entfallen).
Filter-Syntax
Art = '70' or Art = '100'
Leere Felder müssen explizit berücksichtigt werden:
Art <> '70' or PrjNr = NULL
Verfügbare Operatoren:
| Operator | Beschreibung |
|---|---|
< | Kleiner als |
> | Größer als |
<= | Kleiner oder gleich |
>= | Größer oder gleich |
= | Gleich |
<> | Ungleich |
AND | Beide Ausdrücke müssen wahr sein |
NOT | Nachfolgender Wert nicht wahr |
OR | Mindestens ein Ausdruck muss wahr sein |
Performance-Tipp
Wenn nur ein Filter zur Eingrenzung verwendet wird, wird die gesamte Datenmenge erst zum Client transportiert und dort gefiltert. Die Anzahl der Datensätze sollte daher immer vorher über einen Range begrenzt werden.
Vorsicht bei aktiven Filtern
Ist ein Filter aktiv und werden Felder geändert, die zur Filterbedingung gehören, kann es sein, dass der aktuelle Datensatz die Bedingung nicht mehr erfüllt und „verschwindet". Der nächste passende Datensatz wird dann zum aktuellen Datensatz.
Funktionen zum schnellen Feldzugriff
Die Funktionen AcquireFieldList und ReadFieldListValues ermöglichen einen optimierten Feldzugriff bei der Iteration über große Datenmengen.
Schneller Feldzugriff (C#)
AutoDataSet ads = BpApp.DataSetInfos["Artikel"].CreateDataSet();
int iHandle = ads.AcquireFieldList(
"ArtNr:String,Bez1:DataString,Kubez1:String,Einh:String,Vk0.Preis:Float");
object[] oValues = null;
int counter = 0;
ads.First();
while (!ads.Eof)
{
counter++;
oValues = ads.ReadFieldListValues(iHandle);
Console.WriteLine("Datensatznummer: " + counter.ToString());
Console.WriteLine("Artikelnummer: " + oValues[0].ToString());
Console.WriteLine("KuBez1: " + oValues[2].ToString());
Console.WriteLine("Preis: " + oValues[4].ToString());
ads.Next();
}