MDE
Azubi / Student (Deactivated)
Inhaltsverzeichnis
MDE-Toolkit
Inhaltsverzeichnis
Inhaltsverzeichnis3
MDE-Verwaltung5
Aufruf der Verwaltung5
Definition der Abfragen6
Funktionen erfassen7
Programme importieren / exportieren9
Neues Programm definieren10
Grundlegende Einsprungspunkte definieren12
Sprachreferenz13
MDE oder PC13
Sequenzieller Ablauf und Steuerung13
Variablen16
Systemvariablen17
Konfigurationsvariablen lesen und setzten18
Protkollierung26
Arrays27
Properties28
Kommentare28
Die Funktion ASK()28
Die Funktion AddField()29
Maske, Reiter und Container30
Funktionen des Maskenobjektes (PGF)31
Scrollbalken für einen Container (SCR)31
Felder auf die Maske bringen32
Eigenschaften der Textfelder (TXT und EDT)34
Eigenschaften der Bezeichnungsfelder (LBL)35
Eigenschaften von Schaltflächen (CMD, CHK)36
Eigenschaften von Comboboxen (CBO, CBK, CBT)36
Eigenschaften von Comboboxen (OPT)37
Eigenschaften der Grids (GRD, GRH, GRS,GRT und GRM)37
Eigenschaften des Kalenders (CAL)40
Eigenschaften des Grafikobjektes (CHA)40
Felder gruppieren41
Masken und Container leeren42
Hotkeys42
Veränderung von Werten43
Funktions-Menüs auf Maske44
Ribbon-Bar auf Maske (Dynamic-Mode)45
Panels auf der Maske47
Maskensteuerung48
Tastatursteuerung50
Kommunikation / Rückfragen51
Operatoren52
Stringoperationen53
Runden56
Arbeiten mit Datumswerten57
Arbeiten mit Binärfeldern58
Schleifen und Verzweigungen58
Prüfung auf Funktionen59
Zugriff auf die lokale Datenbank59
Zugriff auf die lokale Datenbank mit oFields63
MS Sql-Server als lokale Datenbank64
Zugriff auf Serverprozess64
Kapselung von Servercode66
Zugriff auf das Dateisystem - Dateiupload67
Dateisynchronisation auf alle MDE69
Fotofunktionen – nur für das MDE71
Reports am MDE-PC73
Start externer Programme:74
Fremdsprachigkeit:74
MDE-Jobverarbeitung76
Unterschiede in den Sprachversionen:76
Umstellung von Version 1.xxx auf Version 2.xxx77
Umstellung auf Dynamic-Mode78
_INIT78
MASK78
Meldungen:80
Sonstige Änderungen:81
Der Serverprozess82
Der MDE-Systemstatus83
Die SWSWeb.ini83
Installationsprobleme85
Fallen in der Programmierung SQL85
Programmhistorie85
Empfehlungen90
Parameter in MDE-Jobs90
Hilfsfunktionen zur Analyse90
Granularität von MDE-Jobs91
Fehlerbehandlung und Stabilität91
MDE-Verwaltung
Aufruf der Verwaltung
Die MDE-Verwaltung finden Sie in den Stammdaten unter den Firmen Firmendaten.
Die eigentliche Maske teilt sich über die Register in zwei Teile auf:
Abfragen | Hier werden die Tabellen und Inhalte definiert, die auf die einzelnen mobilen Datenstationen synchronisiert werden sollen. |
Funktionen | In diesem Bereich werden die Masken, der Programmablauf, die Prüfungen und Buchungen definiert. |
Definition der Abfragen
Die Basis der Programme auf den mobilen Geräten sind die Daten aus Professional. Da jedoch nicht alle Daten und vor allem nicht alle Felder der Tabellen in diesen Geschäftsprozessen benötigt werden, definiert man zu beginn, welche Daten mit dem Server in definierten Rhythmen synchronisiert werden sollen.
Name | Dieser Name wird auf dem MDE angezeigt, wenn Daten aus diesem Bereich aktualisiert werden. Ab Version 2.03.004 wird vor dem Namen eine # angezeigt, wenn die Synchronisation bei einem Erstsync unterbrochen wurde. In der Tabelle wird bei jedem Erzeugen der Tabelle eine # vorangestellt. Dieser wird nach vollständiger Synchronisation wieder entfernt. Am MDE-Client ist diese # allerding erst nach einem erneuten Aufruf nach einer Unterbrechung sichtbar. |
Geräte | Über diesen Eintrag kann gesteuert werden, auf welchen Geräten diese Daten zur Verfügung stehen. Bleibt das Feld leer, so werden die Daten allen Geräten zur Verfügung stehen. Mehrere Geräte werden Kommagetrennt eingegeben. |
Tabelle | Tabellenname in Professional-ERP |
Felder | Hier werden Komma getrennt alle gewünschten Felder aufgeführt. Generell werden die Felder ID und CURTIMEST mit übertragen. Diese werden zur Synchronisation benötigt. |
Index-Felder | Hier werden die Felder eingetragen, nach denen auf den mobilen Geräten am ehesten gesucht wird. Hier sind aktuell keine zusammen gesetzten Indexe möglich. |
Filter | Es werden nur die Datensätze übertragen, für den der Filter gültig ist. Um auch Gerätespezifische Daten filtern zu können, kann hier auch auf System- bzw. die Konfigurationsvariablen des MDE zugegriffen werden. Diese werden dann in spitzen Klammern angegeben. and USER=<<MDEUSER>> |
Es ist so, dass immer die Tabelle angesprochen wird, die als erste im Feld TABELLE eingetragen wird. Möchte man aus Index-Gründen den Join umgedreht eintragen, so muss die gewünschte Tabelle in Pipes angegeben werden.
Im folgenden Besipiel wird die Tabelle ARTIKEL synchronisiert:
POSITION join |ARTIKEL| on ART_ARTNR=POS_ARTNR
Tipp:
Immer wenn mehreren Tabellen und mit einem Filter über
curimest
Join
or
nvl
left-join
WFKOPF join WFPOS on WFP_WFKID=WFKOPF.ID and year(WFKOPF.curtimest)>=2019
Tipp:
Bei einer Foxpro-Datenbank kann mit folgendem Trick auch ein Group by erreicht werden. Dabei ist der Group in der Spalte „Tabelle“ einzutragen. Es sind alle Felder plus die qualifizierte ID und der qualifizierte Zeitstempel aufzuführen.
WEKOPF join WEPOS on WEP_WEKID=WEKOPF.ID group by WEK_NUMMER, WEK_LSDAT,WEKOPF.ID,WEKOPF.CURTIMEST
Funktionen erfassen
Die Steuerung des Menüs, der Aufbau der Masken und die Logik innerhalb der Masken werden über das Register „Funktionen“ definiert.
Dabei werden 3 unterschiedliche Ebenen unterschieden:
Funktion | Diese entsprechen einem kompletten Programmteil, der über das Menü am mobilen Gerät aufgerufen werden kann, oder als Folgedialog z.B. hinter einer Auswahl verbergen kann. Spezielle Funktionen: _LOGINWird beim Start der Loginmaske ausgeführt. _INFO zur Anzeige Versionsnummer. _MENUWird nach dem Login ausgeführt. _LOGIN bei der Anmeldung _SHUTDOWN Wird beim Abmelden / Beenden ausgeführt |
Einsprungspunkt (lokal) | Einsprungspunkte sind wie Programmteile, die auf dem lokalen mobilen Endgerät zu bestimmten Zeiten aufgerufen werden. Dabei gibt es vordefinierte Namen, die bei allen Funktionen definiert sind. Diese beginnen mit einem Unterstrich z.B. _INIT, _EXIT, _SHUTDOWN, _WHEN, _VISIBLE Weitere fest definierte Präfixe definieren spezielle Zeitpunkte, wann diese Programmteile ausgeführt werden. Dazu gehören: - VA_Validierung beim Verlassen des Feldes (Bei CBO und CHK beim Ändern) - WH_Wird beim Betreten des Feldes gestartet - VI_Wird beim Anzeigen ausgeführt (Im Einsprungspunkt können über RefThis die Eigenschaften beeinflusst werden. Z.B.: RefThis.Backcolor= RGB(255,0,0) - _WHENWird generell beim Betreten eines Feldes gestartet. - _VALIDWird generell beim Anzeigen eines Feldes gestartet - _SHUTOWN Wird generell ausgeführt, wenn das Programm beendet wird. Bei den beiden letzt genannten Einsprungspunkten steht neben der Variablen vParam1, die den Feldnamen enthält, auch vParam2 zur Verfügung. Hierin steht der Objektname des übergeordneten Objekts. So kann z.B. ein ganzer Container gesperrt werde. Die jeweiligen Endungen entsprechen den angegebenen Objektnamen bzw. aus Gründen der Updatefähigkeit den Feldnamen. Alle weiteren Funktionen sind frei und werden aus diesen Funktionen aufgerufen. |
Einsprungspunkt (Server) | Daneben gibt es Einsprungspunkte, die nicht auf dem mobilen Gerät ausgeführt werden, sondern auf dem Server gestartet werden. Diese werden zur Verbuchung der erfassten Daten auf dem MDE benötigt. Details hierzu werden im Kapitel „Serverprozess“ gegeben. |
Programme importieren / exportieren
Diverse Programme stehen bereits als Vorlagen zum Import zur Verfügung. Diese können genauso importiert werden, wie auch eigene Sicherungen.
Diese Funktionen finden Sie unter dem Menüpunkt „Extras“.
Alle Funktionen aktualisieren | Die Darstellung wird aktualisiert |
Als Vorlage speichern | Speichert die aktuell markierte Funktion in das Vorlagen-Verzeichnis Nur sichtbar, wenn Konfigurationsvariable „MDEVORL=YES“ gesetzt ist |
Funktion als XML-Datei exportieren | Speichert die aktuell markierte Funktion im XML-Format auf die Festplatte |
Funktion als XML-Datei exportieren | Importiert eine zuvor im XML-Format auf die Festplatte gespeicherte Funktion |
Datenregel bearbeiten | Mit dieser Funktion wird der aus den Datenregeln bekannte Editor gestartet und die markierte Funktion geladen. |
Die Konfigurationsvariable MDEXMLEXPTIMER steuert den Export der Timer.
0 = keine Rückfrage und exportieren
1 = keine Rückfrage und nicht exportieren
2 = Rückfrage ob exportiert werden soll
Neues Programm definieren
Über Datei – Neu – Funktion oder das Tastenkürzel [Strg]+[F11] kann eine neue Funktion definiert werden.
Hierzu sind folgende Informationen anzugeben:
Funktionsname | Eindeutiger Name für das Programm. Hierüber werden die internen Verknüpfungen und Aufrufe im Programm abgebildet. |
Funktionsbezeichnung | So wird das Programm im Menü bezeichnet |
Hauptmenüpunkt | Diese Auswahl, die sich nach den Menüpunkten im Professional-ERP richtet, steuert die Zuordnung des Programms zu dem Menüpunkt auf der ersten MDE-Maske. |
Sortierung | Gibt die Position des Programms innerhalb des Menüpunktes an. |
Geräte | Hier können wieder spezielle Funktionen auf nur bestimmten Geräten sichtbar gemacht werden. Bleibt das Feld leer, so sehen alle mobilen Geräte diese Funktion. Mehrere Geräte werden Kommagetrennt eingegeben. |
Bemerkung | Hier werden die Voraussetzungen für das Programm dokumentiert um auch später noch einen Überblick über das Programm zu haben. |
Grundlegende Einsprungspunkte definieren
Nachdem die Funktion nun definiert ist kann mit dem Maskenaufbau gestartet werden.
Über Datei – Neu – Einsprungspunkt oder das Tastenkürzel [F11] kann ein neuer Einsprungspunkt definiert werden.
ToDo | NameEindeutiger Name des Einsprungpunktes |
Lokal / Server | Hier wird festgelegt, ob der Einsprungspunkt auf dem MDE benötigt wird (Lokal) – Erkenntlich am Icon, oder ob die Datenregel auf dem Server zur Verarbeitung der MDE-Anforderung ausgeführt wird (Server) – Icon |
Datenregel | Code der in diesem Einsprungspunkt ausgeführt wird. |
Der erste Einsprungspunkt der in jedem Programm zuerst aufgerufen wird ist _INIT. Dieser wird zur Definition der Variablen, der Steuerung der Maske und dem Maskenaufbau eingesetzt.
Im obigen Beispiel wird bei Erhalt eines Wertes in der Variable cSerachArtNr die noch leere Maske mit dem internen Aufruf Do(„MASK“) aufgebaut. Direkt danach wird die Suche der Daten mit Do(„SEARCH“) gestartet und somit die Maske gefüllt.
Wurde kein Wert übergeben, so wird die Maske lediglich mit Do(„MASK“) leer dargestellt.
Weiteres zu der Programmierung und den Befehlen im Kapitel „Sprachreferenz“.
Sprachreferenz
MDE oder PC
Prinzipiell ist der Aufbau der Funktionen und Einsprungspunkte für MDE und PC identisch. Einige Parameter oder auch ganze Funktionen stehen nur in der einen oder anderen Version zur Verfügung.
Daher haben wir die Teile, die nur für die PC-Version zur Verfügung stehen blau geschrieben.
Die, die nur für die MDE-Versionen verfügbar sind, sind dagegen magenta markiert.
Mit der Einführung der Dynamic Oberfläche sind zudem einige Elemente so nicht mehr verwendbar. Siehe hierzu den Menüpunkt „Umstellung auf Dynamic-Mode“.
Sequenzieller Ablauf und Steuerung
Der Ablauf innerhalb der Einsprungspunkte ist genauso wie zwischen Funktionen streng sequentiell. Das heißt es werden alle Zeilen nacheinander abgearbeitet. Daher ist es wichtig, dass es sogenannte Sprungbefehle gibt.
Sprünge zwischen Einsprungspunkten:
Der Absprung | cRet= do(„MASK“) | Der Code wird an der aktuellen Stelle verlassen und setzt mit der ersten Zeile im Einsprungspunkt fort. Ist dieser dann abgearbeitet wird mit der nächsten Zeile nach do(„…“) Es ist möglich impliziert einen Rückgabewert zurück zu bekommen. Der Funktion können mehrere (max. 10) Parameter mit übergeben werden, die dann als private Variablen vParam1 bis vParam<n> angesprochen werden können. Werden in der aufgerufenen Funktion die Werte über parameter oder lparameter entgegengenommen, so können diese mit sprechenden Namen versehen werden. do(„MASK“,”P1”,”P2”,…) Ein @ vor dem Parameter übergibt den Parameter als Referenz. |
Der entkoppelnde Absprung | SetDo(“SEARCH”) | Im Gegensatz zu do(..) wird in SetDo ein Timer-Event angestartet, das die beiden Prozesse entkoppelt. So kehrt der Programmablauf nach Start des Timer-Events in das Programm zurück und arbeitet dies ab. Nach einer gewissen Wartezeit wird dann der Code im Timer ausgeführt. Diese Methode benötigt man, wenn man z.B. eine Maske schließen will, aber danach noch Code auf der alten Maske ausgeführt werden soll. (VA_...). Zu beachten ist, dass SetDo keinen Rückgabewert haben kann, da ja de Prozesse entkoppelt wurden. |
Der Rücksprung | return cRet | Der Code eines Einsprungspunktes endet mit der letzten Zeile oder dem Befehl return Die Rückgabewerte werden hier im Gegensatz zu DoFunc nicht in Klammern angegeben. |
Sprünge zwischen Funktionen
Der Aufruf | cArtNr=D o Func("ARTINFO " ,.t. ,P1,@P2,… ) | Die Funktion wird an der aktuellen Stelle verlassen und die aufgerufene Funktion wird mit dem Einsprungspunkt _INIT gestartet. Während der Abarbeitung in der aufgerufenen Funktion ist die Variable func() Der zweite Parameter gibt an, ob das aufgerufene Programm in einem eigenen Fenster geöffnet werden soll (nur PC) Es ist darauf zu achten, dass nach der Rückkehr nur die aktuelle Routine abgearbeitet wird, da die Tiefe der aufrufenden Struktur nicht gespeichert wird. Im Laptopmode wird nichts mehr abgearbeitet, da hier die Masken über den 2. Parameter entkoppelt sind (siehe ParentDo() Ab der Version 2.5 werden auch weitere Übergabeparameter angenommen. Es besteht auch die Möglichkeit die Parameter per Ferenz zu übergeben (@P2) Zu beachten ist, dass DoFunc die Private-Variablen des aufrufenden Einsprungs-Punktzerstört. (nicht DoDialog) |
Der Aufruf modal | cArtNr=Do Dialog ("ARTINFO ",P1,@P2,…) | Analog zu DoFunc. Das neue Fenster öffnet sich modal, das heißt das darunterliegende Fenster ist bis zum Abschluss der Arbeiten an dem neuen Fenster nicht bedienbar. Siehe Beschreibung zu DoFunc. Lediglich der zweite Parameter entfällt hier. Achtung: In diesem Fall wird die Maske erst mit dem Ende des _INIT gezeichnet. Es können somit keine FOCUS-Anweisungen in diesem Bereich direkt angegeben werden. Für diesen Falls wird über SetDo(„_INIT_SETFOCUS“) dieses in einen Einsprungspunkt ausgelagert und entkoppelt aufgerufen! |
Der Rücksprung | R eturn(cArtNr) | Hier erfolgt der Rücksprung zu der Zeile DoFunc() /DoDialog() |
Maske schließen | close() return() | Mit Close() wird die aktuelle Maske geschlossen (analog return(cArtNr)) – z.B.: im Init, wenn Maske noch nicht aufgebaut war. |
Logout | Logout() | Schliesst alle offenen Programmfenster und meldet den aktuellen User ab. Diese Funktion ist bereits im Einsprungspunkt _MENU._LOGIN verfügbar. |
Folgeaufruf in aufrufender Maske | ParentDo („Search“) | Mit dem Verlassen der Maske im Laptopmode, weiß die aufrufende Maske nicht mehr, welche Maske nun geschlossen wird. Es findet somit im Standard keine weitere Funktion statt (auch nicht _INIT). Möchte man aber zum Beispiel eine Refresh der Maske starten, so kann man mit diesem Aufruf eine Funktion der Maske aufrufen, die die aktuelle gestartet hat. |
Ab der MDE-Client-Version 1.3.002 ist es auch möglich Einsprungspunkte in anderen Funktionen direkt zu nutzen
Do(„_SHUTDOWN._INIT“)
AsyncJob(„_SHUTDOWN.LOGOUT“)
Variablen
Alle Variablen auf den MDE ohne spezielle Definition sind für das gesamte Programm gültig, daher ist es wichtig alle verwendeten Variablen im Einsprungspunkt _INIT zu definieren und mit einem gültigen / neutralen Wert zu belegen.
Zwischen den Programmen kann man auf die Werte im „anderen“ Fenster zugreifen, indem man im _INIT Bereich über ParentVar() die Zuordnung auf eine lokale Variable macht. (
nWertHier=ParentVar(„nWertDort“
Anmerkung DG: Im Funktionscode würde
Parent()
ParentDo()
Var
Ab der Version 2.xxx können Parameter auch über die Aufrufe Do(), DoFunc() und DoDialog() übergeben werden. Die weitergegebenen Variablen können im _INIT über „parameters“ abgefragt werden. Diese sind allerdings nur Private und müssen daher für das Programm entsprechend umbelegt werden.
Des weitern sind hier die Variablen „case sensitiv“. Das heißt es wird zwischen Groß- und Kleinschreibung unterschieden. cSNR hat einen anderen Inhalt wie cSNr.
Für das Verhalten der Felder auf den Masken ist es wichtig, dass die Variablen mit einem Präfix beginnen, der über den Typ der Variable Auskunft gibt:
c | Zeichenkette | cSNr=“S12345“ |
n | Numerischer Wert | nMenge=1.3 |
l | logische Variable | lOk=.t. |
d | Datumswert | dDat={} – dDat=date() |
Variablen können mit dem Befehl „Release“ zerstört werden, so dass sie in weiteren Programmteilen nicht mehr zur Verfügung stehen.
Release(“nWert1”,”nWert2”) | Die Variablen „nWert1“ und „nWert2“ werden zerstört. Die Anzahl der Variablen die hier angegeben werden können ist nicht auf 2 begrenzt. Alle weiteren müssen nur mit Komma-getrennt angegeben werden. |
Ab der Version 2.5. können Variablen nun auch explizit deklariert werden
Public | Entspricht der bisherigen einfachen Definition ohne Benennung. Die Variablen leben in der aktuellen Funktion. |
Local | Diese Variablen leben nur in dem aktuellen Funktionscode |
Private | Diese Variablen leben nur in dem aktuellen Einsprungspunkt inkl. aller Aufrufe aus diesem heraus via DO() |
Systemvariablen
Folgende feste Variablen sind definiert:
_USERder angemeldete User
_USERIDDie User-ID des angemeldeten Users
_MDEIDID des MDE-Gerätes aus der INI-Datei
_GRUPPEDie Gruppe der das Gerät zugeordnet wurde
_CRZeilenumbruch
_CRLFZeilenumbruch (identisch zu _CR)
_PCDiese Variable ist wahr, wenn PC-Modus aktiv ist
_MDEDiese Variable ist wahr, wenn MDE-Modus aktiv ist
_SQLDiese Variable ist wahr, wenn die lokale Datenbank eine SQL-Datenbank ist
_INFODie Variable nimmt die Versionsnummer der Datenregeln des MDE auf. Gesetzt und angezeigt in _LOGIN._INFO.
_INFOINTERVALNimmt die Anzahl in Sekunden auf, wie die Versionsinfo aktualisiert wird. Default = 5
_VERSIONVersionsnummer der Clientsoftware in Form 2.02.002
_VERSVersionsnummer als Zahl
(Major * 1000000 + Minor * 1000 + Build
)
_LASTSYNCZeitstempel der letzten kompletten Synchronisation
_DEBUGMit dieser Variablen können sogenannte Debug-Meldungen während der Ausführung ausgegeben werden
(Bsp: debug(<variantVariable>,<cHeader>)
_SQLINFOSpeziell ab Version und Android kann man mit zwischen den Werten _SQLINFO=.t. und _SQLINFO=.f. die Ausgabe der Ergebnisse der SQL-Statements als Meldungsfenster anzeigen lassen. Dies hilft bei der Fehlersuche bei der Umstellung auf die SQLite Datenbank
Zudem stehen vor dem Start des eigentlichen Programms und damit der Synchronisation alle Variablen aus der Konfigurationsdatei „swsmde.ini“ zur Verfügung.
Eine Systemvariable im weiteren Sinne lässt sich über die Funktion func() abfragen. Diese Funktion gibt als Rückgabe-Wert zurück, ob die aktuelle Maske direkt aus dem Hauptmenü aufgerufen wurde (.f.) oder aus einer anderen MDE-Maske gestartet wurde (.t.). Damit kann der Ablauf im Programm gesteuert werden.
Beispiel:
Dieses Beispiel steuert im Suchdialog, ob bereits eine Eingabe auf der Vormaske gemacht wurde. Wenn ja wird die Suche sofort durchgeführt. Wenn nein, dann wird erst nach einem Suchbegriff gefragt.
* Maske aufbauen
if func() and !empty(cSearchArtNr)
* Externer Aufruf mit Artikelnummer gleich weiter
Do("SEARCH")
else
* Direkter Start oder Suche extern
cSearchArtNr=""
Do("MASK")
endif
Konfigurationsvariablen lesen und setzten
Jedes mobile Endgerät hat eine eigene Konfigurationsdatei „swsmde.ini“. In dieser Datei werden die Zugriffe auf den Webserver, die lokale Datenbank und sonstige persönliche Eigenschaften gespeichert.
Diese Datei befindet sich bei den MDE in „My Documents“. Bei der PC-Version im Programmverzeichnis der mobilen Installation.
Um auf diese Werte zugreifen zu können gibt es die Funktion
getcfg()
cMDE=getcfg("MDEID","MDE0") | Liest den im ersten Parameter angegebenen Wert aus der INI. Wird dieser gefunden erhält man diesen als Rückgabewert. Ist keiner definiert, so wird der Defaultwert, der als 2. Parameter angegeben wurde verwendet. |
Mit der Funktion
setcfg()
setcfg("MDEID", c AktMDE) | Schreibt den 2. Parameter zu der Eigenschaft des 1. Parameters in die INI-Datei. |
Alternativ können Variablen zur Programmausführung auch an in die Registry des Systems geschrieben werden
Set R eg K ey() | Schreibt Werte in die Registry 1. Wert: Baum in der Registry 2. Wert: Name des Wertes 3. Wert: Wert der gesetzt werden soll Der 1. Wert wird in einer neuen Version optional und somit vom System gesetzt. |
SetReg() | Im Gegensatz zu SetRegKey muss hier zum setzten der Variable nicht der komplette Registry-Baum angegeben werden. Hier reicht es aus erst die Pfade unterhalb von CURRENT_USER\Software\Software Schmiede\Professional\MDE 1. Wert: Pfad unterhalb …\MDE\ 2. Wert: Name des Wertes 3. Wert: Wert der gesetzt werden soll Beispiel: SetReg("Vars","TEST","123") |
cRet= Get R eg K ey() | Liest Werte aus der Registry 1. Wert: Baum in der Registry 2. Wert: Name des gesuchten Wertes 3. Wert: Default-Wert, sofern der Wert nicht gesetzt wurde. Der 1. Wert wird in einer neuen Version optional und somit vom System gesetzt. |
cRet= Get R eg () | Analog zu SetReg() für das lesen der gesetzten Variablen. Beispiel: cRet=G etReg("Vars","TEST","1 11 ") |
Wichtige Inhalte der INI-Datei:
WEBUSER=PROFESS | Name des registrierten Users im Webdienst. Dieser muss passen, sonst wird der Zugriff verweigert. |
WEBPW=SWS | Passwort des registrierten Users im Webdienst. Dieser muss passen, sonst wird der Zugriff verweigert. |
DBLOCATION=___ | Pfad zur lokalen Datenbank. Hier kann auch mit virtuellen Pfaden gearbeitet werden: DBLOCATION=H:\User\%USERNAME%\Week Wird hier ein Pfad angegeben, so wird in diesem eine FoxPro Datenbank aufgebaut. Wird hingegen eine Provider-Info angegeben, so wir diese als Basis für eine SQL-Datenbank genommen: DBLOCATION=Provider=SQLOLEDB;DRIVER=SQL Server;Address= <<Server>> ;Trusted_Connection=Yes; SERVER= <<Server>> ;DataBase=MDESTD |
WEBSERVICE=http://... WEBSERVICE2= http://... | Internet-Adresse des Webservers. Wird der erste nicht gefunden / erreicht, wird versucht über den Zweiten eine Verbindung herzustellen (Intern / extern). Dies wird aber nur einmal beim Start des Programmes geprüft. Ändert sich während des Einsatzes etwas, so muss das Professional-MDE neu gestartet werden. |
DBSIZE=512 | Mit diesem Schalter kann die maximale DB-Größe angegeben. Default ist 128MB |
PW=DBPW | Passwort für die lokale Datenbank. Default ist swsmde. |
PW=c3dz | Passwort für den Zugriff auf die lokale Datenbank, sofern hier SQL zum Einsatz kommt. Eingetragen wird der Verschlüsselte Wert nach Base64 Verschlüsseln: strconv("sws",13) c3dz Entschlüsseln: strconv("c3dz",14) sws |
MAXPWERROR | Anzahl, wie oft ein User sein Passwort falsch eingeben kann, bevor die Datenbank lokal gelöscht wird. Default: 3 |
DBDEL | Ist dieser Schalter gesetzt, so wird nach 3 maliger falscher Eingabe der Login-Daten die lokale Datenbank gelöscht. Die Ausprägung JOB löscht die Datenbank ebenfalls, lässt aber die Tabelle MDEJOB mit eventuell unverarbeiteten Jobs stehen (nur bei lokale SQL-Datenbank) Default: YES |
DBINIDEL | Wird die lokale Datenbank gelöscht, so wird diese mit der nächsten Anmeldung wieder aufgebaut. Mit diesem Schalter kann beim Löschen der Datenbank auch gleich die INI mit gelöscht werden. Damit ist ein Neuaufbau der Datenbank ohne neues Einkopieren nicht mehr möglich. Default: YES |
LOGFILE | Gibt die Datei und das Verzeichnis an, wohin das System die Anmeldeversuche und eventuelle Löschungen der lokalen Datenbank protokolliert. Standard: <<Programm-Verzeichnis>>\SWSMDE.log |
LOGLEVEL | Level der Protokollierung 5 – nur Fehler werden protokolliert 8 – auch erfolgreiche Anmeldzungen werden aufgezeichnet |
MAXSQL | Gibt an, wieviel Zeichen beim Sync pro Insert verwendet werden. Problem ist, dass bei den Funktionen der SQL-Code für den Insert des kompletten Text zu lange würde. So wird der Text zerlegt und beim ersten Mal ein Teil über Insert eingefügt und danach als Update (Text=Al+Neu) upgedatet. So wird die Befehlszeile nicht zu lang. (Geht so nicht am MDE, da nText+Neu ein Fehler aufwirft) =20.000 |
MDEID=MDE1 | Eindeutiger Name des mobilen Gerätes |
SYNCINTERVAL=60 | Angabe der Zeit in Sekunden zwischen zwei Synchronisationsprozessen mit dem Webservice |
SYNCROWS=nnn | Der Schalter gibt die Vorgabe der maximalen Anzahl der zu synchronisierenden Sätze für Tabellen an. Ist der Schalter nicht gesetzt, werden auf dem PC 5000 und auf dem MDE 2000 Sätze auf einmal geholt. Für die Tabelle MDECODE gibt es bereits ein Sonderhandling, dass hier immer nur 50 Sätze auf einmal geholt werden. (Siehe Schalter SYNCROWSCODE) |
SYNCROWSCODE=nnn | Der Schalter gibt die Vorgabe der maximalen Anzahl der zu synchronisierenden Sätze für Funktionen an. Ist der Schalter nicht gesetzt, werden auf dem PC 50 und auf dem MDE 10 Sätze auf einmal geholt. |
WEBTIMEOUT=8000 | Zeitdauer, die das Gerät auf eine Datenbankabfrage vom Webservice wartet. Angegeben ist der Default-Wert. |
CONNECTTIMEOUT=2000 | Zeitdauer, die das Gerät wartet, um die Info zu bekommen, ob eine Verbindung zum Webservice besteht. Angegeben ist der Default-Wert. Wird benötigt, wenn der SYNCINTERVAL zu groß ist und die Verbindung neu etabliert werden muss. Meldung am MDE: „Verbinde mit Server…“ |
POCKET=YES | Hierüber werden das Betriebssystem, und damit der unterschiedliche Maskenaufbau bezüglich der Position des Menüs, definiert. YES = Windows mobile PC NO = Pocket PC |
SCALE=2 | 1 – bei Auflösung 320* 2 – bei Auflösung 640* |
FONTBOLD=YES | Damit wird die Schrift der Maskenobjekte fett dargestellt |
ERRPROT=____ | Schaltet eine Fehlerprotokollierung der SQL-Befehle ein. Angegeben wir Pfad und Dateiname der entstehenden Textdatei. |
COVERAGE=_____ | Ermittelt bei der Ausführung auf dem Gerät die Zeitdauern pro Zeile und gibt diese in dem angegebenen Textdokument aus. |
SQLTEST=_______ | Ermittelt bei der Ausführung auf dem Gerät die Zeitdauern je SQL-Statement und gibt diese in dem angegebenen Textdokument aus. |
PROTMINMS=<Millisekunden> | Wurde die Protokollierung von Sql-Abfragen auf den MDE-Geräten durch Setzen des Schalters SQLTEST aktiviert, so werden alle Sql-Abfragen in einer Textdatei protokolliert. Oft ist es jedoch interessant, nur die Abfragen zu protokollieren, die eine längere Ausführungszeit besitzen. Ist dies gewünscht, so kann dies erfolgen durch Setzen des Schalters PROTMINMS auf die gewünschte Anzahl an Millisekunden, die überschritten werden muss. |
SYNCMULTITS=NO | Wird bei einer Synchronisationsabfrage über mehrere Tabellen gejoint, so werden die Daten der aktuellen Tabelle auch upgedatet, wenn sich der referenzierte Datensatz in der anderen Tabelle geändert hat. Mit setzten des Schalters auf „NO“, kann dies wieder deaktiviert werden. |
SYNCBACK=5 | Anzahl von Sekunden, die beim Sync vor dem gespeicherten CURTIMEST noch mit geschaut wird. |
MDELANG=E | Mit diesem Parameter wird die Sprache für das MDE gesetzt. Das System versucht nun alle Texte aus der Tabelle LANGUAGE zu ermitteln. Wird kein Eintrag für das Objekt gefunden, so wird ein Pflegesatz auf dem Server angelegt. Angezeigt wird in diesem Fall der deutsche Text. Als Voraussetzung muss in der SWSWEB.INI über den Schalter SYSCON die Verbindung zur Systemdatenbank hinterlegt werden und in der Tabelle LANGUAGE muss das Feld LAN_MDE N(1) angelegt sein. Siehe Kapitel „Fremdsprachigkeit“ |
SWSMDEINI=<Ort der Datei> | Es ist möglich mit einer INI, die nur diesen Inhalt hat, auf die korrekte INI zu verweisen. Die INI liegt normalerweise im Verzeichnis \My Documents. Macht man die Geräte Kaltstartsicher, so wird diese immer wieder neu aufgebaut. Daher legt man die korrekt INI auf die Storage Card und verweist in der ursprünglichen auf die aktuelle: SWSMDEINI=\Storage Card\<Pfad>\swsmde.ini |
BUTTONMENU=NO | Hiermit kann das Aussehen des Menüs gesteuert werden. NO - Klassisches TreeView-Menü mit Hauptmenüpunkten YES – Buttonmenü 2-stufig mit der Ebene der Hauptmenüpunkte 2 - Buttonmenü ohne Hauptmenüebene Default ist „NO“ |
LOGINKEYBOARD=YES | Blendet die Tastatur beim Login-Dialog ein |
CLOSEPWKEYBOAR D=YES | Blendet die Tastatur beim Logout mit Passwortaufforderung eine |
SYNCLOGOUT=YES | Automatische Abmeldung wenn gedockt (eigentlich CONNECTED – als für WLan-Lösungen unbrauchbar.). In diesen Fällen wird der Einsprungspunkt _SHUTDOWN in der jeweiligen Funktion (auch MENU) ausgeführt |
CLOSEPW=<PW> | Passwortabfrage, wenn man Professional-MDE beenden möchte. Geht nur nach Eingabe des korrekten Passworts |
LOGOUTUSERCLEAR =YES | Löscht den Benutzernamen beim Ausloggen |
CHANGEPW=YES | Blendet auf der Anmeldemaske den Button zur Passwortänderung ein |
SYNCLOGOUT=YES | bewirkt ein automatisches Abmelden sobald Client gedockt ist und synchronisiert. |
LSYNCCHECK=2 | Steuert die Prüfung, wann zuletzt synchronisiert wurde (Vergleich Zeitstempel letzte Synchronisation Client mit Zeitstempel letzte Synchronisation Server / Soll Probleme unterbinden, wenn auf dem Server eine ältere Datensicherung eingespielt wurde). Ist der Client-TS neuer als der TS auf dem Server, greift der Schalter: 0 = keine Prüfung auf letzte Synchronisation 1 = Meldung Zeitstempelfehler und Abbruch der Synchronisation 2 = Rückfrage ob auf Client alles gelöscht und neu synchronisiert werden soll 3 = Rückfrage ob auf Client alles gelöscht und neu synchronisiert werden soll, bei Verneinung weitere Rückfrage ob Synchronisation trotzdem durchgeführt werden soll 4 = Meldung Zeitstempelfehler und Löschen aller Daten auf Client und neu synchronisieren 5 = sofort alle Daten löschen und neu synchronisieren ohne Meldung |
SYNCDEL=YES | Durch setzen dieses Schalters werden auf dem Client bei der Synchronisation die Datensätze gelöscht, die nicht mehr der Sync-Bedingung entsprechen. |
MDEDEL=YES | Ist dieser Schalter auf NO gesetzt, so werden auf dem Client keine Sätze mehr aus der Tabelle MDEDEL gelöscht. Default: YES. |
RECDEL=<Prot-File> | Wird dieser Schalter gesetzt (Protokolldatei mit Pfad), so werden alle Satzlöschungen auf dem Client von Sätzen, die nicht mehr auf dem Server vorhanden sind, protokolliert. Standardmäßig werden die Satzlöschungen nicht protokolliert |
LABELBOLD=YES | Setzt alle Labels automatisch auf Fett-Schrift |
LABELCOLOR=255,255,255 | Definiert die Default Label-Farbe auf den angegebenen Wert. |
SWSGUI=x | Steuert den MDE-PC Modus 3 = Dynamic Oberfläche |
SCALEDEFAULT=120 | Setz im Dynamic Mode die Maskengröße der Masken mit Ribbonbar auf 120%, sofern keine Registry-Speicherung für den Dialog vorliegt. |
ASKPW=n | Steuert das Verhalten bei der Anmeldung 0 = Default (Anmeldung mit Passwortabfrage) 1 = Ist der Benutzer aus der Betriebssystemanmeldung in Professional angelegt, steht der Maus-Cursor direkt im Passwortfeld 2 = Ist der Benutzer aus der Betriebssystemanmeldung in Professional angelegt, erscheint kein |
USER=xxx | Mit der Konfigurationsvariable USER kann der Benutzer bei der Anmeldung am MDE-Gerät vorbelegt werden. Hierzu kann ein fixer Benutzer angegeben werden oder auch durch Angabe von )( erreicht werden, dass der Benutzer der Windows-Anmeldung verwendet wird. |
WFPPRIVAT=YES | Die Konfigurationsvariable steuert die Sichtbarkeit privater Termine Anderer im Kalender-Control. Ist diese nicht gesetzt, so werden diese Termine nicht angezeigt. Mit YES wird der Block angezeigt, aber kein Inhalt. |
NEXTID=yymmddnnn | Diese Variable setzt das Programm automatisch, wenn vom MDE-Client neue Sätze in die Datenbank eingefügt werden. Dabei wird der Aufzählung immer das aktuelle Tagesdatum vorangesetzt. Wird ein MDE neu aufgesetzt und die INI verändert, so muss dieser Wert gelöscht werden, sofern an diesem Tag noch kein Insert stattgefunden hat. Ansonsten muss der Zähler entsprechend hochgesetzt werden, |
FILESROOT =<<MDEDB>>+\FILES | Über diese Variable kann der Speicherort der vom Server synchronisierten Dateien definiert werden. Default ist das Verzeichnis FILES im Unterordner der MDE-Datenbank. Ansonsten ist der komplette Pfad in der Variablen anzugeben. Z.B.: C:\Temp\Dateien |
FILESDIRLENGTH=3 | Über diesen Schalter kann die Zerstückelung der MDI_REFID in eine Ordnerstruktur beeinflusst werden. Im Standard nimmt das System diese ID, die numerisch 15-stellig ist, und zerteil diese in Pakete der Länge 3. Daraus entsteht die Ordnerstruktur unter der MDI_REFTAB Bsp: REFID=1234567890123145 REFTAB=ART \FILES\ART\123\456\789\012\345\ |
LOGINPWCODE=<Code> | Wird das MDE-Gerät in der Hosentasche transportiert, so kann es relativ schnell vorkommen, dass mehrfach hintereinander ein Login ohne Passwort ausgeführt wird. Da standardmäßig die Datenbank nach der dritten falschen Anmeldung gelöscht wird, kann es hierdurch zu einem Datenverlust kommen. Um dies zu verhindern kann man über den Schalter LOGINPWCODE erreichen, dass der Benutzer vor einem erneuten Anmeldeversuch erst einen Sicherheitscode eingeben muss. Über den Schalter kann definiert werden, was der Benutzer in diesem Fall eingeben muss. Ist der Schalter nicht gesetzt, so erfolgt auch keine Sicherheitsabfrage. |
USERFIELDS | Standardmäßig werden nur bestimmte Felder, die für die Anmeldung auf den MDE-Geräten notwendig sind, auf die Geräte synchronisiert. Erweiterung ist mit diesem Schalter möglich: USERFIELDS=* // alle Felder synchronisieren USERFIELDS=USR_TELE,USR_EMAIL //zusätzliche Felder |
Protkollierung
Funktion
WriteLog(<cText>[,<nLevel>[,<cLogFile>]])
(auch bei der Funktion ist der Default-Level 5, wenn dieser nicht angegeben wird)
LOGFILE=<Pfad+Dateiname> | Log-Einträge vom Programm und über den Befehl WriteLog werden standardmäßig im Programmverzeichnis (PC) oder unter MyDocuments (MDE) in der Datei swsmde.log gespeichert. Soll der Speicherort geändert werden, so kann dies über den Schalter LOGFILE entsprechend eingestellt werden. Default: swsmde.log |
LOGLEVEL=<LEVEL> | Log-Einträge vom System sowie Einträge über WriteLog werden nur dann weggeschrieben, wenn der Level des Eintrags größer als oder gleich wie der Wert im Schalter LOGLEVEL ist. Der Default des Wertes ist 5. Wird der Schalter auf 0 gesetzt, so wird grundsätzlich nichts mehr protokolliert. Beispiel für Protokollierung im Standard: Anmeldungen mit falschem Passwort werden ab Level 4 protokolliert. Erfolgreiche Anmeldungen werden erst ab dem Level 8 protokolliert. Default: 5 |
Arrays
Ein Sonderfall der Variablen stellen sogenannte Arrays dar.
Im MDE-Toolkit sind die Arrays zweidimensional definiert. Das heißt ein Array kann n-Zeilen mit jeweils m-Spalten aufnehmen.
Ein Array muss anfänglich einmal dimensioniert werden:
adim("aVar",6,2) | Dimensioniert das Array „aVar“ mit 6 Zeilen und dazu jeweils 2 Spalten (Werte). |
Die Anzahl der Spalten und Zeilen lässt sich über die Funktion alen() ermitteln:
a len ("aVar", 1 ) | Gibt die Anzahl der Zeilen des Arrays zurück. |
a len ("aVar", 2 ) | Gibt die Anzahl der Spalten des Arrays zurück. |
Wurde ein Array nicht dimensioniert oder gefüllt, so wird der Wert 0 zurück gegeben.
Suchen im Array
n=ascan("aQS",nID,1) | Gibt die Zeile im Array zurück 1. Wert: Array-Name 2. Wert: Such-Wert 3. Wert: Spalte in der gesucht wird (optional – Default: 1) B eachte , Werte in einem Grid- Array sind Strings |
Die Initialisierung und Befüllung der Werte erfolgt am besten in einer Schleife:
nZaehler=1
do while nZaehler<=alen("aVar",1)
aVar[nZaehler,1]=“Wert “+v2c(nZaehler)
aVar[nZaehler,2]=nZaehler
nZaehler=nZaehler+1
enddo
Der Zugriff auf die Werte und deren Darstellung auf der Maske erfolgt wie folgt:
nZaehler=1
do while nZaehler<=alen("aVar",1)
Ask(aVar[nZaehler,1],"aVar["+str(nZaehler)+",2]")
nZaehler=nZaehler+1
enddo
Zu beachten ist, dass der Typ jedes Arraywertes von Zeile zu Zeile unterschiedlich sein darf. So kann z.B. der Inhalt der Arrayelements aVar[1,2] numerisch sein, der Typ von aVar[2,2] ein Datum und aVar[3,1] einen String darstellen.
meld("Typ: "+type("aVar["+str(nZaehler)+",2]"))
Properties
Um Eigenschaften der Maske zentral zu definieren sollten diese an einem zentralen Punkt während des Aufbaus der Maske definiert werden. Hierzu sollte der Einsprungspunkt „SETPROP“ verwendet werden.
Diese Variablen werden für die gesamte Lebensdauer der Masken definiert.
Beispiel:
* Setzte Properties
oProp=CreateObject("PROPS")
oProp.lUserBestKorr=.f.
oProp.
ActiveBusiness
=
1
* Prüfe Recht auf Dialog
oProp.lUserBestKorr=Do("CHECK_RIGHTS")
Beachte, dass die Properties in aufgerufenen Dialogen auch existieren und eventuell überschrieben werden. Daher sin diese sicherheitshalber vor dem Aufruf zu speichern und danach wieder zurück zu sichern.
Beispiel:
Private oPropLocal
* Sichere Properties des aktuellen Dialogs
oPropLocal=oProp
lRet=DoDialog(„L_TEST“)
oProp=oPropLocal
Kommentare
Kommentarzeilen beginnen mit “
*
//
Innerhalb einer Programmzeile kann der Rest mit „
&&
Die Funktion ASK()
Mit der Funktion ASK() werden die Objekte in einem Maskencontainer definiert. Hier kurz der allgemeine Aufbau:
Die Funktion hat 7 Parameter.
1. Wert | Bezeichnung auf Maske (Label) |
2. Wert | Variable – Einsprungspunkt (CMD,CNT) – Cursor (GRD) |
3. Wert | Die ersten 3 Buchstaben geben den Typ des Feldes an (wenn leer=“TXT“) Die Zeichen dahinter ergeben dann den Objektnamen, anhand dessen die Übersetzung der Labels, Buttons etc. gesteuert wird. Bsp: TXTART_ARTNR Hierüber werden auch die Einsprungspunkte WH_, VA_ und VI_ angesprochen.Zur Bewahrung der Updatefähigkeit wird bei der Angabe des Typs mit nur 3 Zeichen, nach wie vor der Variablenname akzeptiert. |
4. Wert | Breite des Feldes |
5. Wert | Höhe des Feldes |
6. Wert | Abstand vom linken Rand |
7. Wert | Abstand vom oberen Rand |
Wird der 7. Parameter weggelassen, so werden alle ASK() untereinander dargestellt. Möchte man mehrere Elemente in der gleichen Zeile ausgeben, so ist dieser zu setzten. Um hier variable zu bleiben stellt das System die Funktion AskTop() zur Verfügung. So macht es nichts aus, wenn weiter oben neue Felder eingebunden werden.
nAskValue=AskTop() | Gibt den aktuellen Abstand zum oberen Rand des übergeordneten Objekts (Maske, Container, …) |
Die Funktion AddField()
Alternativ zu der Funktion ASK() kann auch über die AddField-Methode Objekte auf die Maske gebracht werden.
Im Unterschied zum ASK muss der Programmierer die Positionierung der Felder auf der Maske selbst in die Hand nehmen und Pixelgenau angeben. Dafür können in einer Programmzeile wesentlich mehr Eigenschaften für die Felder gesetzt werden. Beim ASK müssen diese als Eigenschaften des Feldes separat gesetzt werden.
Die Funktion hat 13 Parameter.
1. Wert | Name des Feldes |
2. Wert | Angezeigter Wert |
3. Wert | Tabreihenfolge – Reihenfolge der Felder |
4. Wert | Pixelposition von oben |
5. Wert | Pixelposition von links |
6. Wert | Breite des Feldes in Pixel |
7. Wert | Höhe des Feldes in Pixel |
8. Wert | Feldbreite variabel (.t. oder .f.) |
9. Wert | Feldhöhe variabel (.t. oder .f.) |
10. Wert | Wievieltes Feld in der gleichen Zeile links von diesem Feld, das seine Größe ändern kann + 1 |
11. Wert | Anzahl der Elemente in dieser Zeile, die Ihre Breite ändern können |
12. Wert | Anzahl der Objekte die oberhalb liegen und in der Höhe geändert werden können + 1 |
13. Wert | Anzahl der Objekte in vertikaler Richtung die Ihre Höhe ändern können. |
Beispiele:
cWert="ABC"
AddField("LBLADR_TEXT","TEXT",1,40,10,80,24)
AddField("TXTADR_TEXT",cWert,2,40,100,144,24)
Maske, Reiter und Container
Bei der PC-Variante wird zuerst eine Maske definiert. Auf dieser werden dann diverse Reiter gelegt. Innerhalb der Reiter können über Container die Felder übersichtlich angepasst werden.
PGF | Eigentliche Maske |
CNT | Container |
Beispiele:
Ask("","Main","PGF",,332)
nAskTop=AskTop()
Ask("","Adr1","CNT",380,300)
Ask("","Adr2","CNT",380,300,388,nAskTop)
Funktionen des Maskenobjektes (PGF)
Die Maske kann nach der Definition mit diversen Eigenschaften gestaltet werden:
Beispiel:
Ask("","Main","PGF",,332)
pgfMain.AddPage("Adressen","Adr")
Damit wird auf die Maske ein Reiterobjekt gelegt, das mit ADR referenziert wird.
Damit steht nun ein Einsprungspunkt IN_CNTPAGEADR zur Verfügung, in dem die weiteren Objekte platziert werden können.
In einem Einsprungspunkt _PAGECHANGE kann auf den Wechsel zwischen den Registern regiert werden. Folgende Parameter stehen hier zur Verfügung:
vParam1 | Name des Page-Frames |
vParam2 | Nummer des angeklickten Page-Frames |
vParam3 | Nummer des aktuell aktiven Page-Frames |
Scrollbalken für einen Container (SCR)
Um einen Container auf der Maske mit einem Scrollbalken zu versehen ist ein SCR-Objekt mit dem gleichen Namen wie der Container zu definieren.
Beispiel:
* Container zur Anzeige der QS-Prüfpunkte
nAskCNTPAGE=askTop()
Ask("","QSVALUE","CNTQSVALUE",804,452)
cntQSVALUE.WResize=.t.
cntQSVALUE.HResize=.t.
cntArtikel.VPos=2
* Scrolbar
Ask("","QSVALUE","SCRQSVALUE",16,452,804,nAskCNTPAGE)
SCRQSVALUE.HPos=2
SCRQSVALUE.HResize=.t.
cntxxx.ScrollPos= nPixel | Damit kann man per Code in einem Container mit Scrollbalken positionieren. Beispiel: nAskCNTValue=AskTop() * Speichere Maskenposition aQS[nZaehler,17]=nAskCNTValue Ask("","aQS["+str(nZaehler)+ ",6]",cFieldName,10 &_ ,,250,nAskCNTValue) … cPosFocus="CHK"+padl(aQS[nZaehler,1],20,"0") &cPosFocus. SetFocus () CNTQSVALUE.ScrollPos=aQS[nZaehler,17] |
Funktioniert so auch am MDE. Allerdings wurde der Test nur am PC-Client im MDE-Modus durchgeführt!
Syntax:
nTop=AskTop()
Ask("","VALUES","CNTVALUES",218,224)
Ask("","VALUES","SCRVALUES",20,224,218,nTop)
SCRVALUES.HPos=2
nTop=230
Ask("Beenden","CLOSE","CMD",,40,,nTop)
Felder auf die Maske bringen
Über die Variablennamen werden hier auch die Felder definiert.
Prinzipiell gibt es folgende Feldarten
TXT | einzeiliges Textfeld – erzeugt zusätzlich ein gleichnamiges LBL |
LBL | Bezeichnungsfeld Beachte die Angabe der Länge im Ask() wird immer von einem längeren Inhalt übersteuert. |
CBO | Combobox (manuell bestückt – 2 dimensional) benötigt zur Füllung die Methode AddItem(<cWert>,<cAnzeige>) Der Wert, muss immer als String angegeben werden. Zum leeren des Arrays gibt es die Methode Clear(). |
CBK | Combobox auf Basis der synchronisierten Tabelle Wahl. Ist bei den Eigenschaften als CBO anzusprechen. Angezeigt wird das Kürzel. |
CBT | Combobox auf Basis der synchronisierten Tabelle Wahl. Ist bei den Eigenschaften als CBO anzusprechen. Angezeigt wird der Text des Kürzels |
OPT | Option-Buttons |
EDT | mehrzeiliges Textfeld |
CMD | Schaltfläche |
GRD | Tabelle im klassischen Stil (Basis von .Net) |
GRH | SWS-Tabelle mit Überschrift ohne transparenten Hintergrund |
GRS | SWS-Tabelle ohne Überschrift ohne transparenten Hintergrund |
GRT | SWS-Tabelle ohne Überschrift ohne transparenten Hintergrund |
CHK | Checkbox |
CAL | Kalender-Control (Termine) |
CHA | Grafik-Objekt Ask("","cData","CHAUMSATZ",820,240,4,4) cData=“Was“+_TAB+“Wert1“+_TAB_+“Wert2“ Ask("Zeit","Wert","CHADATA",,200) |
IMG | Objekt zur Bilder-Darstellung Ask(cFile,"Image","IMGFile",232,130) IMGFile.Image=cFile |
Beispiele:
Ask("Artikel","cArtNr",,70)
Ask("Suche"
,"SEARCHART","CMD",,,192,nTop)
Ask("cCols","oSearch","GRD")
Ask("Adressgruppe","cAdrGrp","CBKADR_GRUPPE")
* Da Feldnamen eindeutig sein müssen kann über ~ der gleiche Inhalt ein 2. Mal auf die Maske gebracht werden
Ask("Adressgruppe","cAdrGrp","CBKADR_GRP2~ADR_GRUPPE")
Ask()
Ask("Vertreter","nVertr","CBO")
cbonVertr.Clear()
cbonVertr.AddItem("0","kein Vertreter")
cbonVertr.ShowValue=.f.
für die Tabelle Wahl:
Ask("Sprache","cAdrSpr","CBKADR_SPR",60)
Mit Arrays:
cboCombo.RowSource="aValues"
Alter Definition ohne Objektnamen (nicht Fremdsprachfähig)
Ask("Artikel","cArtNr","TXT")
Neue Definition mit Objektnamen (Fremdsprachfähig)
Ask("Artikel","cArtNr","TXTART_ARTNR")
Wird
Ask()
Prinzipiell ermittelt sich das Programm die aktuelle Position und die optimale Breite aus der Reihenfolge der Definitionen und aus den Geräteparametern.
Möchte man jedoch zwei Elemente auf eine Zeile stellen, so kann man sich über die Funktion
AskTop()
nTop=AskTop()
Zur Korrektur oder fixen Einstellung kann der Funktion auch ein Wert zugeordnet werden:
nTopKorr=AskTop(AskTop()+30)
Hotkeys z.B. für Buttons kann man mit dem kaufmännischen UND („&“) vor dem gewünschten Buchstaben erzeugen. Diese werden dann zur Laufzeit nach dem Betätigen der Taste „ALT“ angezeigt.
Eigenschaften der Textfelder (TXT und EDT)
Die Textfelder kann man nach der Definition mit diversen Eigenschaften belegen:
Beispiel:
Ask("Lagerort","cLager")
txtcLager.Upper=.t.
.UPPER | Eingabe nur in Großbuchstaben möglich |
.READONLY | Sperrt das Feld gegen Eingaben |
.SELECT | Markiert den Inhalt beim Klick in das Feld |
.ENABLED | Aktiviert oder Deaktiviert das Feld |
.TABSTOP | Steuert die Reihenfolge der Felder |
.FONTSIZE | Angabe der Schriftgröße |
.FONTBOLD | Setzt den Inhalt auf fette Schriftart |
.FORECOLOR | Angabe der Schriftfarbe |
.BACKCOLOR | Angabe der Hintergrundfarbe |
.CHANGE=.f. | (generell für alle Felder) Nimmt das Feld aus der Automatik, dass bei einer Änderung das Diskettensymbol aktiv wird (oFields-Bezug) oder umgekehrt (bei fehlendem Bezug) |
Anmerkung:
Die Farben werden über die Funktion
RGB
txt
cLager
.Backcolor=RGB(255,0,0)
Bei der Änderung der Schriftgröße ist darauf zu achten, dass die Abstände auf der Maske vom oberen Rand durch das Programm zu steuern sind. Automatisch werden immer 24 Punkte Abstand eingehalten!
nAskCNT1=AskTop()
Ask("Feld groß","cFeld1","TXTABC_FELD1",60)
TXTABC_FELD1.FontSite=48
nAskCNT1= nAskCNT1+36
Ask("Feld klein","cFeld2","TXTABC_FELD2",60,,,, nAskCNT1)
Eigenschaften der Bezeichnungsfelder (LBL)
Beispiel:
lblcLager.Caption="Lager"
.CAPTION | Bezeichnung des Feldes |
.FONTSIZE | Angabe de Schriftgröße |
.FONTBOLD | Setzt den Inhalt auf fette Schriftart |
.FORECOLOR | Angabe der Schriftfarbe |
.BACKCOLOR | Angabe der Hintergrundfarbe |
Es gibt leider keinen direkten Weg die Schriftgröße zu verändern. Dies kann aber über folgenden Umweg erreicht werden:
setcfg("FontSize","16")
Ask("Prüfungszustand",,"LBLPRU_ZUST")
LBLPRU_ZUST.FontBold
setcfg("FontSize","0")
Hier wird zuerst die globale Schriftgröße hoch gesetzt, das Label auf der Maske dargestellt und gleich danach wieder zurück gesetzt auf den Default-Wert.
Eigenschaften von Schaltflächen (CMD, CHK)
Beispiel:
cmdUPLOAD.Caption="Zugang buchen"
.CAPTION | Bezeichnung des Feldes |
.TABSTOP | Steuert die Reihenfolge der Felder |
.FONTSIZE | Angabe der Schriftgröße |
.FONTBOLD | Setzt den Inhalt auf fette Schriftart |
.FORECOLOR | Angabe der Schriftfarbe |
.BACKCOLOR | Angabe der Hintergrundfarbe |
Eigenschaften von Comboboxen (CBO, CBK, CBT)
Beispiel:
cboLager.Enabled=.f.
.ENABLED | Aktiviert oder Deaktiviert das Feld |
.TABSTOP | Steuert die Reihenfolge der Felder |
.Style=0 | Hiermit wird der Wert der Combobox editierbar für Freitexteingabe (nur im Dynamic-Mode) |
.InputMask=“!!!!“ | Definierung einer Maskierung der Eingabe in Verbindung mit .Style=0 (nur im Dynamic-Mode) 9 = numerisch ! = Großbuchstaben |
Eigenschaften von Comboboxen (OPT)
Option-Buttons haben alle das selbe Datenfeld. Den Wert, der pro Options-Feld zurück gegeben wird kann über den „:“ bei der Definition mit angegeben werden oder später über die Methode .Value definiert werden.
Aktuell können nur numerische Werte zurückgegeben werden.
Beispiel:
nOption=2
&& Setzt in der Anzeige die 2. Option
Ask("Option 1","nOption:1","opt1")
ask("Option 2","nOption:2","opt2")
ask("Option 3","nOption:3","opt3")
opt3.Value=4
&& ab jetzt gibt opt3 4 zurück
.Value | Setzt den Wert, der zurückgegeben wird, wenn die Option gewählt wurde. Dies ist eine Alternative zu der direkten Angabe des Wertes bei der Definition der Darstellung |
.Group | Ordnet das Objekt der entsprechenden Gruppe zu, die dann über WH_ und VI_ summarisch angesprochen werden kann. |
Eigenschaften der Grids (GRD, GRH, GRS,GRT und GRM)
Die Eigenschaften der Grids werden immer mit Grundtyp GRD… angesprochen. Lediglich die Definition unterscheidet das spätere grundlegende Aussehen.
Beispiel:
* Definition der anzuzeigenden Spalten mit den Breiten
cCols="ID;ID;0"+_CR
cCols=cCols+"MPO_KKS;KKS;150"+_CR
cCols=cCols+"MPW_POS;Pos;76"+_CR
*
Mit Nachkommastellen (APP)
cCols=cCols+"
POS
_
STCK
;
Anz.
;76
;999,999.99
"+_CR
* Als Checkbox maskieren
cCols=cCols+"ADR_HANS;H;20;;;#CHK"+_CR
* Anzeige des Grids mit den zugehörigen Eigenschafte
n
Ask("cCols","oSearchRound","GRSROUND",,nGridHeight)
grdRound.NoRefresh=.t.
.RowHeight | Mit dieser Eigenschaft kann die Zeilenhöhe im Grid angepasst werden (nur Dynamic) |
.NoRefresh | Unterbindet die Aktualisierung des Grids bei einem Refresh. Dies verhindert das Neuzeichnen und damit ein eventuelles Flackern am MDE |
. ActiveRow | Weist dem Grid die zu markierende Zeile zu. Diese erhält man beim _GRIDSELECT als vParam2 zurück |
.GoRec(nZahl) | Markiert im Grid die entsprechende Zeile und führt das _GRIDCHANGE Event aus. Bei gesetztem 2. Parameter wird zusätzlich das _GRIDSELECT Event ausgeführt. |
.DelayedChange=.t. | Nur im Multiselect-Grid. Führt nach einem bestimmten Intervall (Default1000 ms) das _GridSelect Event aus. |
.TimerInterval=5000 | Nur im Multiselect-Grid. Definiert die Zeit für die Verzögerung in ms. |
.ActivePos=0 | 0 = Standardverhalten (wie bisher) 1 = aktive Zeile immer am oberen Gridrand anzeigen 2 = aktive Zeile immer am unteren Gridrand anzeigen |
.Multiselect=.f. | Macht aus dem GRH ein Grid mit nur einer Auswahl |
.SetCols(cCols2) | Mit dieser Zuweisung kann man im Nachhinein einem Grid neue Spaltendefinitionen zuweisen. Z.B.: umsortieren oder eine andere Ansicht GRDARTPREIS.SetCols(cColsPreis2) |
.SetGrid(„oSearch“) | Mit dieser Zuweisung kann man dem Grid ein neuer Cursor / neue Datenmenge zuweisen. GRDARTPREIS.SetGrid("oSearchPreis2") |
.SetValue(nRow,cField ,nWert) | Mit dieser Anweisung kann man Werte im Grid ändern ohne diese neu aufzubauen. 1. Wert: Zeile im Grid 2. Wert: Spaltename im Grid (ergibt sich aus cCols) 3. Wert: Wert der neu angezeigt werden soll GRDSEARCHANS.SetValue(1,"ADR_FIRMA2","Test") Es ist zu beachten, dass diese Werte nicht an den Cursor übergeben werden. Es handelt sich um eine reine Ansicht. |
.SelectColor=.f. | Bei Tabellen mit Hintergrundfarbe (Spalte _RGB/_BACK) kann es störend sein, dass die aktiven Zeilen immer mit einem grünen Hintergrund dargestellt wird. Dieser kann über diesen Schalter ausgeschaltet werden. |
.SelectRow(n,.f.) | Mit Parameter wird die n.-te Zeile markiert (.f.) bzw. demarkiert (.t.) Ohne Parameter werden alle Zeilen-Markierungen im Grid (Multiselektions-Grid) entfernt |
Über die VI_ Eigenschaft kann ein Grid über Bedingungen auch ausgeblendet werden.
Eine WH_ Funktionalität gibt es nicht.
Farblicher Hintergrund:
Den Hintergrund der neuen Tabellen kann man durch die Definition der Gridspalte _RGB (_BACK sollte nicht mehr verwendet werden) definieren.
cFields="iif(x=1,"+v2c(nRGB1)+","+v2c(nRGB2)+")
+00000000000
as _
RGB
Der Spaltenname im Dynamic-Mode ist hingegen _RGB!!!
Anmerkung:
Die Auswertung des Grids erfolgt über zwei spezielle Einsprungspunkte_GRIDCHANGE – wird beim Klick in das Grid ausgeführt
_GRIDSELECT – wird bei einem Doppel-Klick in das Grid ausgeführt
Anmerkung:
In nicht DynaicMode wird das _GRIDSELECT nach dem _GRIDCHANGE auch bei einem Einfach-Klick ausgeführt.
Dabei werden 3 Parameter belegt:
vParam1 | Gibt den Namen des Grids aus der Definition der Anzeige zurück. Darüber wird es möglich auf mehrere Grids auf einer Maske zu reagieren. |
vParam2 | Enthält die Zeilennummer aus dem, dem Grid zugeordneten Cursor, und dient als Sprungmarke zur Anzeige / Ermittlung der gewünschten Daten. |
vParam3 | Enthält den Spaltennamen auf dem der Klick ausgeführt wurde. |
if vParam1="GRDDONE"
oRowDone=GetRow(oSearchDone,vParam2)
nWertID=GetField(oRowDone,"ID")
e
ndif
Anmerkung:
Analog den Parametern in aufgerufenen Masken können die Variablen auch bei Standard-Events zur besseren Lesbarkeit mit Namen versehen werden und auch die Gültigkeit von private auf local geändert werden (z.B. lparameter lcGrid,lnRecNo)
Eigenschaften des Kalenders (CAL)
Ask("","Main","CALMAIN",988,480,4,4)
calMain.WResize=.t.
calMain.HResize=.t.
calMain.User=cActUser
* Setzte Starttag
calMain.Date=dAktAnzeigeStart
* Tagesstart und Ende
calMain.DayStart="08:00"
calMain.DayEnd="18:00"
Notwendige Felder:
Für das Kalender-Control sind bestimmte WFPOS-Felder notwendig:
WFP_WFTID,WFP_GRPID,WFP_GRPSTA,WFP_ADAUER,WFP_RDAUER,WFP_RAUM,WFP_BERTYP
Sichtbarkeit privater Termine:
Hier ist die Konfigurationsvariable WFPPRIVAT wichtig. Ist diese nicht gesetzt, so werden private Termine Anderer gar nicht angezeigt. Mit YES wird der Block angezeigt, aber kein Inhalt.
Eigenschaften des Grafikobjektes (CHA)
Mit diesem Objekt können einfache grafische Darstellungen realisiert werden.
Der Aufruf des Ask erfolgt mit den folgenden Parametern
Name der x-Achse
Name der y-Achse
Name des Objektes
Positionierung und Größe wie üblich
Dazu werden dem Grafikobjekt die einzelnen Messreihen als 2-dimensionale Array zugeordnet (ADD). Der erste Wert entspricht dem x-Wert und der zweite dem y-Wert der Grafik
.Add() | Mit dieser Methode wird diesem Grafikobjekt eine Messreihe zugeordnet. 1. Wert: Bezeichnung der Messreihe 2. Wert: Gibt die Form der Grafikdarstellung /// 1 = Balken /// 2 = Linien /// 3 = Linie mit Punkten und Stufen 3. Wert: Farbe der Messreihe in rgb() 4. wert: Das Werte-Paare-Array als String |
.Draw() | Zeichnet die Grafik |
.Clear() | Löscht die komplette Grafik |
.Show0 | Über diese logische Variable kann angegeben werden, ob die Null-Linie angezeigt werden soll. |
.YDecimals | Gibt an, wieviel Dezimalstellen bei der y-Achsen-Beschriftung verwendet werden sollen |
Beispiel:
Ask("Zeit","Wert","CHADATA",,200)
chaData.Show0=.f.
chaData.YDecimals=1
* Arrays wurden im SEARCH belegt
if recc
(oSearchUGOG)>0
chaData.
Add
("Max. Messbereich",2,
rgb
(255,0,0),"aDataOG")
chaData.
Add
("Min. Messbereich",2,
rgb
(0,255,0),"aDataUG")
endif
chaData.
Add
("Messwerte",3,
rgb
(0,0,255),"aData")
chaData.
Draw
()
Felder gruppieren
Allen Feldern gemein ist die Möglichkeit diese zu Gruppen zusammen zu fassen.
txtFeld1.Group="grpContainer1"
So vereinfach es sich gemeinsame Eigenschaften und Verhalten zu realisieren.
In dem nun zur Verfügung stehenden Einsprungspunkten
VA_GRPxxx
WH_GRPxxx
VI_GRPxxx
Tipp: Innerhalb des Einsprungspunktes stehen die beiden Parameter vParam1 und vParam2 zur weiteren Unterscheidung zur Verfügung.
vParam1 | Enthält den Feldnamen, der den Einsprungspunkt ausgelöst hat. Z.B.: txtFeld1 |
vParam2 | Enthält die übergeordnete Einheit zum Feld. Z.B.: den Container cntEigensch1 |
Masken und Container leeren
Mit der Funktion CLEAR() können alle Elemente der Maske oder mit Angabe eines Container-Objektes des Containers wieder entfernt werden.
Die Funktion Clear für die Maske hat 4 Parameter.
1. Wert | Name der Maske |
2. Wert | Breite der Maske in Pixel |
3. Wert | Höhe der Maske in Pixel |
4. Wert | Name unter welchem die Maskeneinstellungen in der Registry gespeichert werden. |
Die Funktion zum leeren eines Containers hat keine Parameter. Zu beachten ist, dass nach dem leeren des Container auch noch zusätzlich der vorhandene Einsprungspunkt IN_<<Containername>> aufgerufen wird, der die Informationen zum Neuzeichnen enthält.
Beispiele:
Clear("Terminverwaltung",1020,900,"frmP_WFL_FRM")
cntAdresse.Clear()
Hotkeys
In der PC-Version, kann man einigen Objekten einen Hotkey zuordnen, der bei der Bedienung über die Tastenkombination Alt+<Hotkey> alternativ zum Anklicken des Objektes verwendet kann.
Der Hotkey wird bei der entsprechenden Caption durch Voranstellen des kaufmännischen und („&“).
Folgende Objekte werden unterstützt:
CMD
AddBar
Beispiel:
Ask("&Suche","SEARCHART","CMD",,,192,nTop)
Veränderung von Werten
Wird ein Wert auf der Maske verändert, so wird die Eigenschaft _CHANGED vom Programm gesetzt. Diese wird im DynamicMode dazu genutzt die Speicher- und Undo_Buttons zu aktivieren. Achtung aber nur wenn die Referenz für die Masken-Felder ein oFields Bezug ist.
Dies gilt, sofern nicht das Feld explizit von dieser Methodik ausgenommen ist
CBOANZEIGE.
Change=.f.
Pro Feld wird die globale Eigenschaft lChanged beim Betreten auf .f. gesetzt. Wird nun eine Änderung an dem Feld gemacht, so wird diese Variable .f. und kann somit im entsprechenden Valid ausgenutzt werden.
Wird das Feld jedoch aus dem Code gefüllt und soll über eine VA_-Funktion geprüft werden, so muss diese Variable manuell gesetzt werden.
Beispiel Adress-Suche mit nachgestellten VA_:
oFieldsVOR.VOR_KDNR=DoFunc("P_ADRSEARCH",.t.)
* Setzte Änderung, so dass Valid reagiert
lChanged=.t.
DO("VA_TXTVOR_KDNR")
Dass die Valids der Felder auch immer durchlaufen wurden, das heisst die Variable lChanged (die über den return Wert beeinflusst werden kann) wieder auf .t. steht kann über die Funktion Valid() geprüft werden. Diese Funktion sorgt dafür, dass zuerst die VA_ durchgeführt wurden. Somit kann z.B das parallel abarbeiten eines VA_ und eines WRITE() unterbunden werden.
Valid sorgt dafür dass in dem Feld, in dem aktuell der Cursor steht das Valid durchlaufen wird. Sollte das Valid .f. zurückgeben und das Feld somit nicht verlassen werden können, so gibt auch die Valid-Funktion .f. zurück.
Beispiel beim Start der Speicherroutine:
* Abfangen, dass sich VA_ und Speichern überholen
if !Valid()
return
endif
do case
case pgfMain.ActivePage=1
Do("WRITEVOR")
case pgfMain.ActivePage=2
Do("WRITEPOS")
endcase
Funktions-Menüs auf Maske
Mit der Addbar-Methode kann man auf der Maske in die Menüstruktur „Extras“ weitere Funktionen integrieren.
Beispiel:
AddBar("Ersatzetikett Lager","LABEL")
Die Funktion hat 2 Parameter
1. Wert | Angezeigter Name |
2. Wert | Einsprungspunkt der bei Aktivierung gestartet wird |
Eigenschaften:
.Text | Damit kann der Text der Funktion im Programmablauf beeinflusst werden. |
.Enabled | Bietet die Möglichkeit Funktionspunkte auf Inaktiv zu setzten. |
In der PC-Version lassen sich über die AddBar-Methode ganze Strukturen von Menüs aufbauen.
Zuerst wird auf oberster Ebene der angezeigte Menüpunkt definiert
AddBar("Datei","","barFile")
1. Wert | Angezeigter Name |
2. Wert | Einsprungspunkt der bei Aktivierung gestartet wird. Dieser ist hier leer. |
3. Wert | Gruppennname der Struktur |
In diese Struktur hinein können nun weiter Menüpunkte gelegt werden.
AddBar("Speichern","Save","barSave","barFile","F10")
AddBar("Verwerfen","Undo","barUndo","barFile")
1. Wert | Angezeigter Name |
2. Wert | Einsprungspunkt der bei Aktivierung gestartet wird. |
3. Wert | Gruppennname der damit erzeugter Struktur |
4. Wert | Gibt an zu welcher übergeordneten Struktur der Menüpunkt gehört. |
5. Wert | Funktionstaste um den Menüpunkt direkt starten zu können. |
Die einzelnen Punkte können über den Einsprungspunkt VI_BARxxxx aktiv bzw inaktiv gesetzt werden.
Zudem besteht in diesem Bereich die Möglichkeit den Text durch setzen der Variablen cBarText zu verändern.
if cISOTyp="ISO"
cBarText="&Kalibrierungstyp auf nonISO ändern"
else
cBarText="&Kalibrierungstyp auf ISO ändern"
endif
Ribbon-Bar auf Maske (Dynamic-Mode)
Im Dynamic-Mode verwendet man anstatt den Menüstrukturen sogenannten Ribbon-Bars.
Für den Aufbau dieser Ribbons stehenfolgende Funktionen / Befehlse zur Verfügung:
AddPage("padRecord", "Datensatz") | Erzeugt einen leeren Ribbon Datensatz auf der Maske Der erste Parameter gibt an, wie dieser im Programmcode angesprochen werden kann. Der Zweite gibt die Bezeichnung im Reiter des Ribbon auf der Maske an. |
AddGroup("padRecord", "popAllg","Allgemein" | Mit dieser Funktion richtet man eine logische Gruppe im Ribbon ein. Erkenntlich wird dies auf der Maske durch die senkrechten Teilungsstriche. Parameter gibt den Bezug zur Page an Parameter gibt an, wie die Gruppe weiter im Programmcode angesprochen wird Parameter gibt die Bezeichnung unterhalb der Gruppe an |
AddButton("popAllg", "barNew","Neu","F9", "F9","NEW",1,1) | Mit dieser Funktion können nun Funktions-Buttons auf dem Ribbon platziert werden. Parameter gibt den Bezug zur Gruppe im Ribbon an Parameter gibt an, wie der Button weiter im Programmcode angesprochen wird Parameter gibt die Beschriftung unterhalb des Buttons an Parameter gibt den Shortcut zur Bedienung des Ribbons mit der Tastatur an Parameter ist der Anzeigetext (Tooltip) für die Shortcuts Parameter gibt den Einsprungspunkt an, hinter dem sich der Code zur Ausführung nach dem Betätigen dieser Auswahl verbirgt Parameter ist die Nummer des anzuzeigenden Icons. Parameter: letKeysActive (bool) Ist der Wert gesetzt (.t.), so bleiben Hotkeys bei der Anzeige in der Ribbonbar auch mit Betätigen des aktuellen Hotkeys stehen. So kann über die Tastatur eine ganze Reihe von Befehlen Funktionsaufrufen nacheinander ausgeführt werden. Dies geht so lange, bis ein Button der Ribbonbar betätigt wird, bei welchem die Eigenschaft auf .f. gesetzt ist. |
AddSubButton("barNewPos", "barNewP","Position","F12", "F12","NEWPOS",1) | Soll sich hinter einem Button eine weitere Auswahl zur Verfügung gestellt werden, so wird dies über die Sub-Buttons gemacht. Dies bedingt zuerst einen neutralen Button Funktionsaufruf. Auf diesen referenzieren dann die Sub-Buttons. Parameter gibt den Bezug zur Hauptbutton der Gruppe an Parameter gibt an, wie der Sub-Button weiter im Programmcode angesprochen wird Parameter gibt die Beschriftung neben dem Button an Die weiteren Parameter sind identisch mit denen des Buttons |
REFRESHSTATUS() | Mit dieser Funktion wird die Ribbon-Bar neu gezeichnet und alle Valids- und Whens- der einzelnen Objekte neu durchlaufen. |
Beispiel des Aufbaus der Ribbon-Bar im Vorgang (P_VOR_FRM)
Clear
("Auftragsbearbeitung",956,760,"frmP_VOR_FRM")
* Ribbon-Bar
AddPage("padRecord","Datensatz")
AddGroup("padRecord","popAllg","Allgemein")
AddButton("popAllg","barNewVor","Neu","F9","F9","NEWVOR",1,1)
AddButton("popAllg","barNewPos","Neu","","","",1,1)
AddSubButton("barNewPos","barNewP","Position","F12","F12","NEWPOS",1)
AddSubButton("barNewPos","barNewT","Textposition","","","NEWTEXT",1)
AddButton("popAllg","barWrite","Speichern","F10","F10","WRITE",3,1)
AddButton("popAllg","barUndo","Änderungen verwerfen","ESC
APE
","ESC","UNDO",4,1)
AddButton("popAllg","barSearchVor","Suchen","F7","F7","VORSEARCH",12,1)
AddGroup("padRecord","popRecord","Datensatz")
AddButton("popRecord","barDel","Löschen",,,"DELREC",5,1)
AddGroup("padRecord","popAnsicht","Ansicht")
AddButton("popAnsicht","barRefresh","Aktualisieren",,,"UNDO",28,1)
Panels auf der Maske
In der PC Version und im Dynamic-Mode (SWSGUI=3) können Panels in der Masken-Statuszeile eingebaut werden.
AddPanel(1) | Definiert ein Panelobjekt. Am besten in dem Einsprungspunkt _INIT. Der Parameter gibt die Ausrichtung an 0 = links 1 = rechts Eine Benamung der Panels erfolgt hier nicht. Die Reihenfolge der Definition ist hier ausschlaggebend. |
SetPanel(1,2,cInhalt) | Füllt das Panel 1. Parameter – links oder rechts 2. Parameter – Welches Panel 3. Parameter - Inhalt |
AddPanel darf nur einmal aufgerufen werden und sollte somit im _INIT benutzt werden. Hiermit werden die Panels definiert.
*Panels für VOR_NETTO und VOR_BRUTTO
AddPanel(1)
AddPanel(1)
* Fülle Panels
SetPanel(1,1,"Netto "+
allt
(
transform
(oFieldsVOR.VOR_NETTO,"999,999,990.99"))+" "+
allt
(oFieldsVOR.VOR_WAEHR))
SetPanel(1,2,"Brutto "+
allt
(
transform
(oFieldsVOR.VOR_BRUTTO,"999,999,990.99"))+" "+
allt
(oFieldsVOR.VOR_WAEHR))
Maskensteuerung
Folgende Möglichkeiten existieren um die Maske neu aufzubauen, mit aktuellen Werten zu versehen und den Cursor durch Programmlogik zu setzen:
Clear(„Lager-Info“ ,800,640 , “frmABC“ ) | Leert die Maske komplett und setzt die Überschrift in den Programmkopf. Der zweite Parameter gibt die Breite der Maske an. Der dritte die Höhe. Der letzte Parameter kann angegeben werden, wenn sich das System die letzte Position und Größe in der Registry speichern soll. |
_SCREEN.Height=600 | Mit dieser Eigenschaft kann zur Laufzeit die Höhe der Maske vergrößert oder verkleinert werden - z.B.: Zur Anzeige der Suchergebnisse. |
_SCREEN.Width=800 | Mit dieser Eigenschaft kann zur Laufzeit die Breite der Maske vergrößert oder verkleinert werden. |
_SCREEN.Caption=xxx | Überschrift der Maske neu zuweisen. Aktuell ist kein Auslesen des Wertes möglich. |
_screen.MaxHeight=260 _screen.MinHeight=260 _screen.MinWidth=nStartWidth _screen.MaxWith=0 | Über diese Werte lassen sich die Maskenbegrenzungen einstellen. Das heißt die Masken können dann nicht größer als …MAX und nicht kleiner …MIN werden. Die Werte lassen sich nicht nur setzen, sondern auch abfragen. |
_screen.WindowState=0 | Über diese Methode lässt sich der Fensterstatus abfragen / setzten: 0 = Normal 1 = Minimiert 2 = Maximiert (bis …MAX) |
_screen.scale=1.3 | Setzt die Skalierung der Maske auf 130% |
_SCREEN.CLOSABLE | Verhindert das Schließen der Maske über das rote X Da man am MDE immer nur eine Maske hat, führt das Entfernen des X dazu, dass es bei einer eventuellen Rückkehr in einen Vordialog dort auch verschwunden ist. Möchte man den Wert nicht bei jedem Dialog auf .t. setzen, so empfiehlt es sich das Handling um den Funktionsaufruf zu bauen. _screen.clos able=.f. lRet=DoFunc(…) _ screen.clos able=.t. |
Refresh() | Ordnet allen Feldern auf der Maske die neuen Inhalte zu. |
cntAdresse.Refresh() | Ordnet allen Feldern in dem angegebenen Objekt die neuen Werte zu. Ohne die gesamte Maske neu aufzubauen. |
txtcLager.Focus() | Setzt den Focus in das Feld „cLager“ |
close() | Schliesst die aktuelle Maske |
logout() | Schliesst nicht nur die Maske, sondern führt darüber hinaus noch einen Logout durch. Man findet sich danach wieder in der Anmeldemaske für Professional-MDE |
Beispiel für einen einfachen Maskenaufbau:
Clear("Lager-Info")
nTop=AskTop()
Ask("Lagerort","cLager",,70)
txtcLager.Upper=.t.
Ask("Suche","SEARCH","CMD",,,182,nTop)
AddBar("Ersatzetikett Lager","LABEL")
Refresh()
txtcLager.Focus()
Beispiel: Variable Größenveränderliche Masken (Suchmasken)
nStartWidth=656
nStartHeight=564
Clear("Adresse suchen",_screen.Width,_screen.Height)
Ask("Matchcode","cSearchMatch","TXTSEARCH_MATCH",382+(_screen.Width-nStartWidth))
Tastatursteuerung
Da ein Programm nicht nur über die Buttons und Funktionen auf der Maske bedient werden kann gibt es die Möglichkeit die Tastatur mit einzusetzen.
KEY_xxx | Einsprungspunkt, der beim Druck der Taste <xxx> aufgerufen wird. Bsp: KEY_ENTER oder KEY_ESCAPE |
KEY_F15=ESCAPE | In der SWSMDE.INI kann über diese Zuweisung einer speziellen Funktionstaste das Verhalten der dahinter angegebenen Taste zugeordnet werden. Erläuterung: Da es auf manchen MDE die ESC-Taste nicht gibt, wird hier der Taste F15 das Verhalten für ESC zugeordnet. Im Code wird der Einsprungspunkt KEY_ESCAPE ausprogrammiert. Wird nun die taste F15 betätigt wird der Code hinter KEY_ESCAPE ausgeführt. |
KeyStop() | Verwirft ab dem Setzten die Eingaben über die Tastatur. Dies verhindert Eingaben im Textfeldern und die Einsprungs-Punkte KEY_xxx. |
KeyResume() | Nimmt nun wieder die Eingaben über die Tasten entgegen. |
KeyClear() | Löscht den aktuellen Tastatur-Puffer |
Kommunikation / Rückfragen
Zur Interaktion mit dem User gibt es folgende Funktionen:
meld("Bitte Artikel scannen") meld(„Vorgang $AUF$ gelöscht“,poropob(„AUF“ ,VOR_AUF),“Info“) | Meldung an den User durch einen einfachen Meldungsdialog. Parameter ist der anzuzeigende Text Parameter wird nur ausgewertet wenn dieser mit poropob() Wichtig ist die bei fremdsprachigen Versionen, da die Texte der Meldungsfenster automatisch in die LANGUAGE eingehen. Wird hier der variable Textteil mit im 1. Parameter angegeben, so würde der Text in jeder Variation in der LANGUAGE zum übersetzten ankommen. In der Funktion poropob() Parameter bzw. wenn dieser nicht angegeben wird und der 2. Parameter ein String ist, ist dies die Headline der Meldung (optional) |
nRet=j_n_a(„Wirklich löschen?“,“J“) | Rückfragedialog mit den zwei Schaltflächen „JA“ und „NEIN“. Zurückgegeben wird bei „JA“ eine 1 – bei „NEIN“ eine 0. Anzuzeigender Text Default-Button – mit Semikolon getrennt kann die Zeit angegeben werden, wann die Frage automatisch beantwortet wird ("J;25") (Zeitsteuerung geht nur, wenn auch der 3.Parameter = Neuer Rückfragedialog) Kopfzeile der Maske Ausrichtung 1=zentriert Hintergrundfarbe als RGB angegeben Farbe des 1. Buttons als RGB Farbe des 2. Buttons als RGB Schriftgröße der Buttons Im PC-Modus wird bei einer Parameterzahl <=3 die kleine Messagebox() aus dem Standard verwendet. |
nRet=show(cKKSInfo, "Info",1) | Infodialog analog meld(), nur dass hier die ganze Maske dazu verwendet wird und weitere Buttons zur Verfügung stehen. Parameter: Anzuzeigender Text Kopfzeile der Maske Text-Ausrichtung (0=links – 1=mittig – 2=rechts) Hintergrundfarbe als RGB angegeben Farbe des Buttons als RGB Schriftgröße des Textes |
Showme(cVars) | Showme zur Anzeige von Variablen (Arrays) analog Standard-Showme n=adir("aFiles",cFilePath) showme("aFiles[3]") |
BEEP (nInt) | Spielt einen Ton. Es stehen 6 verschiedene Töne zur Verfügung (nInt von 1 bis 6) |
SOUND (cWave) | Spielt die angegebene Wave-Datei ab. Die Angabe muss inkl. Pfad erfolgen. |
Waitstart() | Setzt den Cursor als symbolische Sanduhr. Die Sanduhr wird automatisch vor einem meld() beendet. |
Waitstop() | Setzt den Cursor wieder zurück |
Operatoren
Folgende Operatoren werden unterstützt:
^ | Potenz |
* | Multiplikation |
/ | Division |
+ | Addition |
- | Subtraktion |
= | Gleich (Werte) |
== | Identisch (Stringvergleich) |
# | ungleich |
> | Größer (Alle Stringvergleiche ab 2.6.006 auch für Alpha-Werte) |
< | kleiner |
>= | größer gleich |
<= | kleiner gleich |
and | logisches und |
or | logisches oder |
! | logisches nicht |
empty() | Zeigt an, ob der übergebene Wert gefüllt ist |
min/max | Mit den Funktionen kann der Mnimal- bzw. Maximalwert von zwei numerischen Werten ermittelt werden nErg=min(nWert1,nWert2) |
Es gelten die üblichen mathematischen Priorisierungen im Ablauf: „Punkt vor Strich“ und „Zusammenfassung von Klammerungen“
Stringoperationen
Um Inhalte von Feldern zu vergleichen gibt es einige Funktionen:
type(„ cText “) | Gibt den Typ des angefragten Objektes zurück: U – undefiniert C – Character N – Numerisch T – Date-Time D - Datum O - Objekt |
v2c(nMenge ) | Wandelt jeden Variablen-Typ in einen String um. |
v2c (dInvDat, nWert ) | Wandelt ein Datumswert in einen String. Wird der zweite Parameter weggelassen, so findet lediglich eine Typwandlung statt. Mit 1 werden die Hochkommata für Abfragen gesetzt Mit 2 wird ein Datumswert in das SQL-CE Format gewandelt Mit 4 entscheidet der Parser, ob es eine PC-Umgebung auf FoxPro-Tabellen oder eine SQL-CE Datenbank ist. Dementsprechend ist der String wie 1 oder 2 formatiert. |
str(nWert,12,2) | Wandelt den Wert aus nWert in einen String mit 12 Vorkomma- und 2 Nachkommastellen um |
allt( cText ) | Schneidet Leerzeichen am Anfang und Ende des Strings ab. |
ltrim(cText) | Schneidet Leerzeichen am Anfang des Strings ab. |
rtrim(cText) | Schneidet Leerzeichen am Ende des Strings ab. |
Transform(nWert,“999,999.99“) | Wandelt einen Wert in einen String um unter Berücksichtigung der Picture-Klausel (nur Numerische Werte umgesetzt!) |
padr( cText , 3 0 , “-„) | Ergänzt den übergebenen String rechts bis zur angegeben Anzahl (2. Parameter) mit Leerzeichen bzw. dem Zeichen an 3. Stelle. |
padl(cText,30 , “-„) | Ergänzt den übergebenen String links bis zur angegeben Anzahl (2. Parameter) mit Leerzeichen bzw. dem Zeichen an 3. Stelle. |
upper(cText) | Wandelt den angegeben String in Großbuchstaben um. |
lower(cText) | Wandelt den angegeben String in Kleinbuchstaben um. |
len(cText) | Gibt die Anzahl von Zeichen des übergeben Strings zurück. |
at(cSuch,cText), atc(cSuch,cText) | Gibt die Stelle im String cText zurück, an der die Such-Zeichenkette cSuch das erste Mal auftritt. at() ist dabei Case-Sensitiv. |
substr(cText,10,2) | Gibt die Zeichen aus dem String (1. Parameter) ab dem zweiten Wert aus. Ist kein dritter Parameter angegeben, dann bis zum Ende des Strings, ansonsten so viele Zeichen, wie der 3. Wert angibt (hier also 2). |
left(cText,5) | Gibt die 5 (2 Parameter) linken Zeichen aus dem String zurück |
right(cText,5) | Gibt die 5 (2 Parameter) rechten Zeichen aus dem String zurück |
inlist(cLand,“D“,“E“,“F“) | Prüft, ob der Wert in ersten Parameter (cLand) in der weiteren Aufzählung (ab 2. Parameter) vorhanden ist. Wenn Ja, so gibt die Funktion .t. zurück. Sonst .f. |
Strtran(cText,cSuche,cErsetze) | Ersetzt in dem String cText cSuche cErsetze |
Subv(cText,nVorkommen,“,“) | Gibt den Teilstring aus cText |
Addbs(cText) | Prüft den übergebenen Text, ob dieser mit einem \ endet. Ist dies nicht der Fall, so wird der String um einen \ erweitert. |
va l l (cText) | Wandelt eine Zahl in den numerischen Wert unter Berücksichtigung der länderspezifischen Trenner („.“, „,“) um. Handelt es sich um keinen numerischen Ausdruck, so wird 0 zurückgeliefert. (SWS-UDF!) |
v al(cText) | Interne Funktion von Foxpro und die gibt einen Fehler aus, wenn cText nicht numerisch darstellbar ist. |
asc(cBuchstabe) | Gibt den ASCII-Wert des Arguments zurück |
chr(nAscii) | Gibt das Zeichen für den ASCII-Wert zurück |
eval( "oFields."+aFields[nField,1] ) | Wertet den als String übergebenen Ausdruck aus und gibt den Wert zurück. In diesem Beispiel interpretiert eval aFields[nField,1] VOR_AUF oFields.VOR_AUF eval(oFields.VOR_AUF) 602100 |
chr(13)+chr(10) | Wandelt den angegeben ASCII-Code in das korrekte Zeichen um. Dies auch für nicht druckbare Zeichen. |
vget(@cParam," MNG ", str(nMNG) ) nMenge= vget(@cParam,"MNG", ,“N“) | Setzt bzw. fragt Werte aus einer Wertepaarstruktur ab. Die Werte werden immer als String gespeichert. 1. Parameter – Name der Struktur 2. Parameter – Name des Wertepaares 3. Parameter – Wert 4. Parameter – Erwarteter Typ der Abfrage N = numerisch D = Datum T = Datum mit Uhrzeit Wir der 3. Parameter angegeben, so wird dieser gesetzt. |
Runden
Numerische Werte können mit folgenden Funktionen gerundet werden:
int(nWert) | Schneidet den Nachkommawert ab |
round(nWert,nStelle) | Rundet den Wert mathematisch auf bzw. ab. Der Wert nStelle ist optional und gibt die Nachkommastelle auf welche gerundet werden soll. Default ist 0. Negative Werte wirken sich auf die Vorkomastellen aus. |
ceil( nWert ) ceil ing (nWert) | Gibt den nächst höheren Integerwert in Bezug auf nWert zurück. |
abs(nWert) | Gibt den positiven Wert zurück als INT bzw. Double. |
Arbeiten mit Datumswerten
Zum Arbeiten gibt es einige Funktionen:
dDate=date() | Ordnet der Variablen das heutige Datum zu |
dDate=date(2011,12,24) | Ordnet der Variablen das kurze Datum {24.12.2011} zu. Werte müssen numerisch übergeben werden (vall()) |
dDate=datetime() | Ordnet der Variablen das heutige Datum inklusive der Uhrzeit zu |
cTime=Time() | Gibt die aktuelle Uhrzeit als String zurück |
Ticks(tDateTime) | 64-Bit-10tel Mikrosekunden seit 1. Januar 0001 |
S econd(dDate) | Ergibt die Sekunden aus der datetime-Variablen |
M inute(dDate) | Ergibt die Minuten aus der datetime-Variablen |
hour(dDate) | Ergibt die Stunden aus der datetime-Variablen |
day(dDate) | Ergibt den Tag aus einer date- bzw. datetime-Variablen |
m onth(dDate) | Ergibt den Monat aus einer date- bzw. datetime-Variablen |
year(dDate) | Ergibt das Jahr aus einer date- bzw. datetime-Variablen |
ttod(t Date Time ) | Gibt aus dem Datumsstring inkl. Zeit (31.12.2010 12:01:25) nur das Datum (31.12.2010) zurück. D.h. es findet einen Konvertierung des Typs von DAteTime zu Date statt |
ttoc(tDateTime) | Konvertiert den Date-Time-Wert in einen Text-String |
dtoc(dDate) | Konvertiert den Datumswert in einen Text-String |
dDat=date()+nTage | Addiert die Anzahl Tage zu dem aktuellen Tagesdatum |
dDat=goMonth (date(), nMonate) | Addiert die angegebene Anzahl von Monaten zum Tagesdatum |
dDatetime()+nSeconds | Addiert die Anzahl der Sekunden zu einem Date-Time-Wert |
Dow(date()) | Gibt den Wochentag des Datums als Zahl zurück (Sonntag=1) |
Arbeiten mit Binärfeldern
Zum Auswerten von Binärfelder gibt es die Funktion bittest. Mit dieser kann ermittelt werden, ob in einem String das x. Bit gesetzt ist.
bittest(b Wert,nBit)=1 | Ermittelt aus dem binären Wert b Wert nBit |
bWert=bitset(bWert,nBit) | Setzten des Bits nBit bWert |
bWert= b itclear (bWert,nBit) | Löscht in der Variablen bWert nBit |
b itand (bWert1,bWert2) | Gibt den Bitwert zurück der in beiden Bitwerten gemeinsam gesetzt ist |
bitor (bWert1,bWert2) | Gibt den Bitwert zurück der entweder in dem einen oder anderen Bitwert gesetzt ist |
Beispiel / Exkurs:
Aufbau Bitzahlen: Bit 0 (2^0) ungesetzt 0 Bit 0 gesetzt 1
Bit 1 (2^1) gesetzt addiert 2
Bit 2 (2^2) gesetzt addiert 4 etc
Sind 1 und 2 gesetzt, so hat bWert den Wert 6
Bei bitand(5,6) ergibt sich 4, da 5 binär = 101 und 6 binär = 011. Das heisst bei beiden ist nur das 3. Bit gemeinsam gesetzt also 2^2=4
Bei bitor(5,6) sind dies die Bits 1,2,3 also 2^0+2^1+2^2=7
Schleifen und Verzweigungen
Es gibt folgende Schleifen-Konstrukte
If - Schleife | If lOk=.t. nMenge=1 else nMenge=0 endi f |
Einzeilige if-Abfrage | nRet=iif(nTest=1,“Ok“,“nOk“) |
Do Case | do case case nMenge<100 nRabatt=5 case nMenge<1000 nRabatt=8 otherwise nRabatt=10 endcase |
Do-While – Scheife | do while nMenge<100 nMenge=nMenge+1 enddo |
Do-While Schleifen können mit
exit
enddo
Prüfung auf Funktionen
Es gibt zwei Funktionen, mit denen geprüft werden kann, ob in der aktuellen Installation Funktionen beziehungsweise Einsprungspunkte vorhanden sind:
HasFunc("P_XXX") | Prüft, ob die Funktion „P_XXX“ importiert wurde und vorhanden ist. |
HasCode(„P_XXX._INIT“) | Prüft, ob es den Einsprungspunkt _INIT in der Funktion P_XXX gibt. |
Zugriff auf die lokale Datenbank
Zur Arbeit mit der lokalen Datenbank gibt es einige Befehle:
oSearch=GetC ursor() | Erzeugt einen Cursor als Rückgabewert. 1. Parameter - 0=lokale Datenbank 1=Serverdatenbank 2. Parameter – Felder 3. Parameter – Tabelle (from) 4. Parameter – Bedingung (where) 5. Parameter – Sortierung (order by) 6. Parameter – Gruppierung (group by) 7. Parameter – Having 8. Parameter – Name des Cursors für SQL-Server. „~“ Erzeugt den Cursor auf dem SQL-Server. „~NAME“ vergibt zusätz-lich den Namen. Anmerkung: Ist der erste Parameter nicht numerisch, so wird dieser als “Felder” genommen und alle Parameter verschieben sich nach vorne. Wird der 8. Parameter gesetzt kommt kein Cursor zurück, sondern ein String. Auf den Cursor auf dem Server kann dann über einen weiteren Select auf ~NAME eventuell #NAME zugegriffen werden. In diesem Fall muss der Speicher wieder manuell freigegeben werden mit: DelCursor(oSearch) |
DelCursor(oSearch) | Löscht den angegebenen Cursor-Inhalt. Vernichtet aber das Objekt nicht. Wichtig ist dies vor allem für Cursor, die auf dem SQL-Server erzeugt wurden. Aber auch bei lokale – sehr großen – Cursorn kann hiermit der Speicherbedarf reduziert werden. Lokal: DelCursor(oSearch) SQL: DelCursor(„~NAME“) |
recc(oSearch) | Gibt die Anzahl der Ergebnissätze des angegebenen Cursors an. |
oRow=GetRow(oSearch,1) | Liest aus dem angegeben Cursor die Zeile, die an zweiter Stelle übergeben wird. |
lRet=DelRow( oSearch ,nRow) | Löscht die angegebene Zeile (nRow) aus dem lokalen Cursor. Als Rückgabewert wird der Erfolg der Löschung erhalten. |
cBez=GetField(oRow,"ART_BEZ1") | Liest das an zweiter Stelle angegeben Datenbankfeld aus der übergebenen Reihe und übergibt diesen als Rückgabewert, |
lRet= sqlexec( “<<SQL>>” ) | Befehl zur direkten Ausführung von SQL-Statements. Beginnt der Befehl mit #, so werden Fehler nicht ausgegeben, sondern ignoriert. |
create("TAB","ID","N(15)") | Erzeugt eine lokale Tabelle. 1. Parameter - Tabellenname Danach kommen paarweise, Komma getrennt angegeben die Feldnamen und Feldtypen. |
index( “TAB”,”FELD” ) | Legt für die Tabelle TAB FELD |
i nsert("TAB","ID",123) | Fügt in die Tabelle “ TAB |
UPDATE("TAB",123,"ID","321") | Ändert in der Tabelle „TAB“ den Satz mit der ID=123 (immer 2. Parameter ist die ID) ab. Dabei werden die Felder und Werte wieder paarweise, Komma getrennt angegeben. |
UPDATE(„TAB“,cWhere,“WERT“,0) | Ändert in der Tabelle „TAB“ für alle Sätze die der Where-Bedingung unterliegen, das angegeben Feld auf den angegebenen Wert. Dabei werden die Felder und Werte wieder paarweise, Komma getrennt angegeben. (Massenupdate) |
d elete("TAB",321) | Löscht den Satz der ID=321 (immer 2. Parameter ist die ID) in der Tabelle „TAB“ |
drop("TAB") | Löscht die Tabelle „TAB“ aus der lokalen Datenbank. |
pofda(„TAB.ID“ ,.t. ) | Prüft, ob das angegebene Feld ID TAB Der zweite Parameter ist optional. Normalerweise werden die Werte von pofda() gecacht. Möchte man das umgehen (z.B. für temporäre Tabellen, so übersteuert der 2. Parameter = .t. das Caching. |
wah_get(„ADR_LAND“,„DE “) | Funktion gibt in Abhängigkeit des optionaen 3. Parameters den Wert aus der Tabelle WAHL zurück. Achtung immer als String! Parameter gibt den Bereich (WAH_FELD) in dem gesucht wird Parameter ist das Kürzel als Suchbegriff Parameter. Ist dieser nicht gefüllt, so wird die Bezeichnung des Kürzels zurück gegeben. Wird hier ein Wert übergeben, so wird dieser als Feldname in der Tabelle WAHL gewertet und der entsprechende Feldinhalt dieser Spalte zurückgegeben. |
Beispiel zur Artikelsuche:
* Prüfe Artikel
cFields="ART_BEZ1"
cWhere="ART_ARTNR='"+cArtNr+"'"
cFrom="ARTIKEL"
oSearch=GetC
ursor(0,cFields,cFrom,cWhere,"ART_ARTNR",)
if recc(oSearch)#1
* Kein gültiger Artikel
meld(“Kein gültiger Artikel”)
return
endif
oRow=GetRow(oSearch,1)
c
Bez=GetField(oRow,"ART_BEZ1")
Beispiel Massenupdate
cWhere="MPW_MPKID="+v2c(nKopfID)
cWhere=cWhere+" and MPW_MPMID>0"
cWhere=cWhere+" and MPW_ZEIT="+v2c(d1900,4)
UPDATE("MP_WERT",cWhere,"MPW_MPMID",0,"MPW_USRID",_USERID)
Zugriff auf die lokale Datenbank mit oFields
Um mit dem Datenobjekt oFields zu arbeiten, stehen zwei weitere Funktionen zur Verfügung:
oFields= GetFields ( oSearch ,nRow ) | Für jedes Feld das in der Feldliste des Cursor steht wird nun im Objekt oFields ein Wertepaar definiert. (Name der Spalte, Inhalt der Spalte). Der zweite Parameter ist optional und gibt den Datensatz im Cursors an, die eingelesen wird. Wird der Parameter weggelassen, so werden die Werte des ersten Datensatz gesetzt. |
oFields= GETBLANK (oSearch ) | Im Falle, dass man für jedes Feld einen leeren Wert initialisieren möchte, so kann dieser Aufruf genutzt werden. |
aFields = afields (oFields) | Erstellt eine mehrdimensionales Array(n,2) aus dem oFields-Objekt. aFields[n,1]Name der Tabellenspalte aFields[n,2]Wert in der Tabellenspalte |
Der Zugriff auf die Werte erfolgt dann einfach durch Angabe des oFields-Objektes und dem Tabellenfeld angehängt durch einen Punkt.
Zu beachten gilt, dass die Zuweisung von zwei oFields Objekten keine getrennten Objekte erzeugt, sondern nur Referenzen. Möchte man zwei eigenständige Objekte erhalten, so muss der Cursor zwei Mal explizit zugewiesen werden.
Zwei unabhängige Objekte:
oFieldsVOR=GetFields(oSearch
,nRow
)
oFieldsVOROld=GetFields(oSearch
,nRow
)
Objekte verbunden über eine Referenz. Das heißt eine Änderung im Einen führt diese automatisch im anderen nach!
oFieldsVOR=GetFields(oSearch
,nRow
)
oFieldsVOR
1
=
oFieldsVOR
Beispiel:
* Lese nun Adressinfos nach - VOR_KDNR
oSearch=
GetCursor
(0,"*","ADRESSEN","ADR_NUMMER="+v2c(oFieldsVOR.VOR_KDNR))
if recc
(oSearch)>0
oFieldsADR=GetFields(oSearch
,nRow
)
else
oFieldsADR=GetBlank(oSearch
,nRow
)
endif
cSprache=oFieldsADR.ADR_SPR
MS Sql-Server als lokale Datenbank
Das „group by“ geht auf die Daten vor der Gruppierung. Aus diesem Grund kann man noch keine Aliase verwenden. Aber dennoch Qulifiziert zugreifen!
„Order by“ und „Having“ wiederum werden erst nach der Gruppierung ausgeführt, wodurch auf die Alias zugegriffen werden kann bzw. beim Having sogar muss.
Beispiel:
if
_PC
cFields=cFields+",nvl(LAP2.LAP_NAME,' ') as STELLP"
else
cFields=cFields+",case when LAP2.LAP_NAME is null then ' ' else LAP2.LAP_NAME end as STELLP"
endif
cFrom=cFrom+" left join LAGPLATZ LAP2 on LAP2.ID=LAGPLATZ.LAP_LAPID"
cOrder=cOrder+",STELLP"
cGroup=cGroup+",LAP2.LAP_NAME"
Zugriff auf Serverprozess
Der Austausch zwischen den mobilen Geräten und dem Server erfolgt über den Austausch von Wertepaaren, die in einer Zeilenstruktur abgebildet werden.
Dazu wird auf dem mobilen Gerät diese Struktur gefüllt und dann dem Server übergeben. Hier unterscheidet man zwei Arten.
ASyncJob() | Die Aufgabe wird an den Server übergeben. Auf eine Antwort wird nicht gewartet. Das heißt am MDE geht die Arbeit sofort weiter. Folgende Parameter hat die Funktion Serverfunktion, die aufgerufen werden soll Parameter-Liste zur Übergabe Bei mehreren Job-Verarbeitungen kann die Gruppe für die Jobverarbeitung mitgegeben werden. lNoSync – Nach der Übergabe startet nicht ein Sync, sondern erst, wenn wieder die Zeitspanne für den nächsten Sync erreicht ist. Ohne Gruppe kann der Wert auch als 3. Parameter übergeben werden. (Default ist .f.) Beispiel A SyncJob("PONLINE",cParam, “ADM“ , .t. ) |
SyncJob() | Die Aufgabe wird an den Server übergeben. Der aktuelle Prozess wartet nun bis eine Antwort vom Server kommt. Erst dann geht die Arbeit am MDE weiter. Folgende Parameter hat die Funktion Serverfunktion, die aufgerufen werden soll Parameter-Liste zur Übergabe Bei mehreren Job-Verarbeitungen kann die Gruppe für die Jobverarbeitung mitgegeben werden. Zeit in Millisekunden, wann der Job bei einer eventuellen Nicht-Erreichbarkeit abbricht. Beispiel cOutput=SyncJob("PONLINE",cParam,,1000) |
lRet=Checkweb() | Mit diesem Aufruf kann der aktuelle Verbindungsstatus zum Webserver abgefragt werden. Der Rückgabewert ist wahr, wenn aktuell eine Verbindung zustande kommt. Veraltet – bitte Online() verwenden |
lRet= Online() | Mit diesem Aufruf kann der aktuelle Verbindungsstatus zum Webserver abgefragt werden. Der Rückgabewert ist wahr, wenn aktuell eine Verbindung zustande kommt. |
Syncstop ( .t./.f. ) | Stoppt die Synchronisation. Als Parameter existieren folgende Werte: .f. oder ohne Parameter = Sync-Abbruch wenn aktueller Sync beendet .t. oder 1 = Sync-Abbruch nach aktueller Tabelle 2 = Sync-Abbruch sofort 3 = Sync-Abbruch durch Benutzer (kann nur durch SyncStart(.t.) wieder aufgehoben werden. Beendet Sync nach aktueller Tabelle. Default ist .f. |
Syncstart() | Startet die Synchronisation wieder, sofern diese nicht mit SyncStop(3) generell gestoppt wurde. |
Sync S tart ( .t. ) | Startet die Synchronisation wieder, sofern diese mit SyncStop(3) generell gestoppt wurde. |
SyncStatus() | Funktion zur Abfrage des aktuellen Sync-Zustandes. Folgende Rückgabewerte wurden definiert:0 = Sync ist angeschaltet1 = Sync wird nach Abschluss des aktuellen Sync-Laufs beendet2 = Sync wird nach Abschluss aktuelle Tabelle beendet3 = Sync wird sofort beendet4 = Sync ist angehalten5 = Sync wurde durch den Benutzer angehalten (StopSync(3)) |
SyncReset(nMinuten) | Korrigiert die interne Zeit der letzten Synchronisation um n Minuten. So können fehlende Sätze neu synchronisiert werden. – Sonderfall |
SyncResume() | Ist als Alternative zu SyncStart() zu sehen. Währen bei SyncStart() sofort die nächste Synchronisation beginnt, läuft bei SyncResume() der Timer zur nächsten Synchronisation im Hintergrund weiter. Erst wenn dieser 0 wird, beginnt der nächste Sync. |
Die Aufgaben werden am Server in der Tabelle MDEJOB gespeichert und über den Serverdienst, der über „Dienste“ – „MDE Job-Verarbeitung“ gestartet wird.
Beispiel für die Inventurübergabe:
vget(@cParam,"INVDAT",dInvDat,"D")
vget(@cParam,"INVLIST",cInvList)
vget(@cParam,"LAGER",cLager)
vget(@cParam,"ARTNR",cArtNr)
vget(@cParam,"MENGE",str(nMenge))
vget(@cParam,"SNR",cSNr)
ASyncJob("INSERT_INV",cParam)
Kapselung von Servercode
Immer wieder kommt es vor, dass man einen Codeteil aus mehreren Serverfunktionen aufrufen möchte. Dies geht hier über die Anlage einer separaten Serverfunktion, die dann über eine Datenermittlung in den aktuellen Code eingebunden wird.
Der gewünschte Code sei in unserem Beispiel in der Funktion „KAPSELUNG“ unter dem Server-Einsprungspunkt „KASPELCODE“ gespeichert.
Die eigentliche Funktion ist in der Tabelle „MDEFUNC“ definiert. Der Code an sich ist in der Tabelle „MDECODE“ mit dem internen Bezug MDEFUNC.ID = MDECODE.MDC_MDFID und dem entsprechenden MDC_TODO-Namen zu finden.
* Lese gekapselte Datenregel ein
cDR=RefThis.
Load
("KAPSELCODE.KAPSELFUNC")
*
Führe
Datenregel mit übergebenen Parameter aus
cError=ex(cDR,,,
P1
,P2,P3,…)
Über die Standard-Funktion
ex()
Alternativ kann auch folgender Aufruf verwendet werden_
*Alternative
RefThis.Do("
KAPSELCODE.KAPSELFUNC
",
P1,P2,P3,…
)
*
Oder
RefThis.SyncJob("_EXT
_
LAGER.UMLAGERN",cParam)
Bei Timer-Funktionen ist Stand Mai 2019 kein
RefThis.Do
RefForm.oBusiness.Do
Zugriff auf das Dateisystem - Dateiupload
Für den Austausch von Dateien zwischen dem Client und dem Server, wie zB der Übertragung einer Unterschrift oder PDF wurden einige Befehle umgesetzt:
file(cFile) | Diese Funktion prüft, ob die Datei, die inklusive Pfad in der Variablen cFile existiert. Der Rückgabewert ist .t. oder .f. |
d= AttrFile(cFile, nMode) | Gibt zu einer übergeben Datei die Zeitattribute je nach Modus zurück nMode = 1: Erzeugung (wird beim Kopieren verändert) nMode = 2: Letzter lesender Zugriff nMode = 3: Letzter schreibender Zugriff |
makedir(cPath) | Legt das in cPath angegeben Verzeichnis an. Existiert dies bereits macht das Programm nichts |
removedir(cPath) | Löscht das angegeben Verzeichnis |
fcopy(cFile,cDest) | Kopiert die Datei cFile, die inklusive Pfad anzugeben ist, in das Zielverzeichnis. Als 3. Parameter kann angegeben werden, ob die Datei im Zielverzeichnis einfach überschrieben wird (Default .f.) Mit dem 4. Parameter kann man sagen, ob die Datei kopiert (.f.) oder aber verschoben (.t.) werden soll. Default ist hier .f. |
n= adir( „ aFiles “ ,cPath F , lRecursive ) | Die Funktion gibt die Anzahl der Dateien als Rückgabewert zurück. Parallel erhält man die Dateinamen im angegebenen Array zurück. Enthält der 2. Parameter einen „.“ (Punkt), so wertet das System diesen Teilstring als Filter auf die Dateien (also auch ‚R*.pdf‘). Wenn nicht, werden alle Dateien im Verzeichnis zurück gegeben. Der 3. Parameter ist optional und gibt an, ob auch Unterverzeichnisse mit durchsucht werden sollen. Wird dieser nicht angegeben, dann wird nur das angegebene Verzeichnis durchsucht. cFilePath="C:\temp" oder cFilePath="C:\temp " aFiles=““ adim(„aFiles“,0) n=adir("aFiles",cFilePath) meld("Anz. : "+v2c(n )+"/ "+v2c(aFiles[1 ])+" /"+v2c(alen("aFiles"))) |
DelFile(cFile) | Löscht die angegebene Datei. Veraltet – bitte FDEL |
fdel(cFile) | Löscht die angegebene Datei. |
cGUID=addupload(cFile) | Diese Funktion wandelt die Datei in übertragbares Format und trägt automatisch die Werte für den Upload in die Tabelle MDEUPLD ein. Über einen zweiten Parameter kann angegeben werden, ob die Datei kopiert oder verschoben wird (lMove)??? Als Rückgabewert erhält man dann den Namen, den die Datei später im Upload-Verzeichnis (UPLOADPATH in SWSWEB.ini) auf dem Server haben wird. Die Realisierung der Umwandlung passiert während der normalen Jobverarbeitung, wenn diese ausgeführt wird. |
cImage=FileToHex(cFile) | Wandelt die Datei cFile in einen Hex-String um und speichert diesen in der Variablen z.B.: cImage. Diese kann dann als Parameter in cParams an den Serverprozess übertragen werden. (ALT – neu siehe addupload) |
cStr=FileToString(cFile) | Wandelt die Datei cFile in einen String um und speichert diesen in der Variablen z.B.: cStr. |
Beispiel für die Rückwandlung des Hex-Strings in das Ursprungsbild:
cHex=vget(@cInput,"Sign")
cFile=tmp_file("jpg")
hex2file(cHex,cFile)
shellex(cFile)
Beispiel für die Übertragung einer PDF-Datei :
Am MDE-PC
…
cGUID=addupload(cPDFFile
* Übergebe Daten an Server
cParam=""
vget(@cParam,"FILE",cGUID)
…
ASyncJob("REPUPLOAD",cParam)
Am Server
…
cUplFile=vget(@cInput,"FILE",,"C")
cZielPfad=
“E:\
....\mdereport\"
makedir(cZielPfad)
fcopy(cQuellPfad+cUplFile,cZielFile)
…
Dateisynchronisation auf alle MDE
Eine weitere Variante Dateien zwischen den beiden Enden (Clients und Server) zu tauschen und synchron zu halten stellt die Verwendung der Tabelle MDEFILE dar.
Alle Dateien, die in der Tabelle MDEFILE auf dem Server vermerkt sind, werden automatisch über den Synchronisationsprozess der Clients auf diese übertragen.
In diesem Fall ist die Tabelle auf dem Server führend. Wird hier z.B. eine neuere Version eingetragen, so führt dies automatisch zum Download auf den Client.
Dennoch gibt es auch auf dem Client Möglichkeiten die Struktur zu analysieren und gegebenen Falls die Synchronität wieder herzustellen.
Sollen Dateien aus der Synchronisation herausgenommen werden, so muss der Datensatz aus der MDEFILE über das Datenobjekt
goData.MDEFILE.Delete(
„
ID=xxx
“
)
Funktionen auf dem Server:
nID=setMDEfile() | Mit dieser Server-Funktion wird die Tabelle MDEFILE gefüllt bzw. aktualisiert, wenn die Datei bereits vorhanden war. Der Rückgabewert der Funktion ist die ID des Satzes in der MDEFILE 1. Wert: Datei inkl. Pfad die synchronisiert werden soll 2. Wert: Referenztabelle – Oberste Ebene des Speicherortes auf den MDE 3. Wert:Referenz-ID – Fasst alle zusammengehörigen Dateien in der Speicherstruktur auf dem MDE zusammen. Die ID wird in Teilstrings zerlegt und ergibt daraus die Speicherstruktur. Siehe dazu die Konfigurationsvariable FILESDIRLENGTH (3) 4. Wert:Dateiname zur Identifikation der Datei in der MDEFILE 5. Wert:Typ der Datei – Eine weitere Möglichkeit Dateien zusammen zu fassen. Siehe hier auch die Anmerkungen zu aDirMDE() 6. Wert:Filterbedingung für Geräte und Gerätegruppen 7. Wert:Info über den Erfolg der Funktion als Referenz<leer> wenn alles OK-3 wenn Datei oder Verzeichnis nicht existieren Beispiel: ?setmdefile("c:\temp\ art .txt"," ART ",123, "DGDAT1","INFO",,@cMessage) |
CheckMDEFile() | Future Wish Diese Funktion sollte auch als Server-Funktion bereit gestellt werden. Die Funktionsweise sollte wie folgt sein: Die Funktion nimmt alle in der Servertabelle MDEFILE hinterlegten Sätze und prüft Ist Datei vorhanden und Zeitstempel passt – mache nichts Ist die Datei vorhanden und Zeitstempel ist falsch (egal wie) – Schreibe Zeitstempel neu in Datei und setze curtimest hoch Ist die Datei nicht mehr vorhanden – lösche den Satz in MDEFILE und schreibe einen MDEDEL-Satz Somit wären auch Änderungen am Server einfach wieder den MDE’s mitteilbar Wenn nicht dann: cPathF="C:\TEMP\*.*" dime aFiles(1,5) adir("aFiles",cPathF) ?aFiles[x,1]Name der Datei ?aFiles[x,2]Größe der Datei in Bytes ?aFiles[x,3]Datum des Zeitstempels ?aFiles[x,4]Uhrzeit des Zeitstempels ?aFiles[x,5]Dateiattribute |
Für die Übertragung vom Server gibt es noch die Konfigurationsvariable MDEDLB64.
Dieser Schalter ist bei SQL-Servern per Default auf YES. Ansonsten NO.
In diesem Fall erfolgt der Dateitransfer über ein Base64-Feld. Dieses ist Bestandsteil der Tabelle MDEFILE (MDI_DATA).
Funktionen auf dem Client:
Auf dem Gerät sind nun die Dateien in einem Verzeichnis FILES unterhalb des Datenbank-Ordners zu finden. Dieser Ordner kann über die Konfigurationsvariable FILESROOT auch an jeden beliebigen Ort verschoben werden.
C heck MDEF iles () | Diese Funktion prüft alle Dateien, die in der lokalen Tabelle MDEFILES stehen, ob diese auf dem MDE vorhanden sind und ob der Zeitstempel in der Tabelle mit der Datei zusammenpasst. Ist dies nicht der Fall, so wird die Datei erneut zum Download angefordert. Dies erfolgt über das Feld Status=0. |
cPath= GetMDIPath(nID) | Diese Funktion gibt den kompletten Pfad inkl. Dateiname zu der gespeicherten Datei als String zurück. E:\MDEDB_DM\FILES\ ART \000\000\000\000\123 \ART.TXT |
cGUID=GetGUID() | Gibt als String die nächste GUID (Dateiname für Dateiupload) zurück. Diese Funktion wird nicht mehr benötigt, da dies heute bereits automatisch in der Funktion addUpload() integriert ist. |
nFiles=aDirMDI (<Array>, <MDI_REFTAB>, <MDI_REFID>, <Dat.Typ>) | Analog dem Befehl adir() mit dem man Dateien in einem Verzeichnis auflisten kann. Dieser Parameter nimmt einem im Prinzip die Aufsplittung der Unterverzeichnisse ab. 1. Wert: Array zur Aufnahme der Dateinamen 2. Wert: Referenztabelle – Oberste Ebene des Speicherortes auf den MDE 3. Wert:Referenz-ID 4. Wert:Hierüber kann man einen Filter definieren, welche Files gewünscht werdenA*.pdf – alle Dateien beginnen mit A und der Endung PDF*.pdf – Alle PDF-DateienINFO – alle Dateien mit MDI_TYPE=INFO |
Beispiel:
* Lese Dateien auf dem MDE
nFiles=0
*
Definiere das Array
aDir
=""
adim("aDir",0)
* Lese gewünschte Dateien ein
nFiles=adirmdi("aDir","ART",123,"*")
* Prüfe Ergebnis
meld("Anz.: "+v2c(nFiles)+" / "+v2c(
alen
("aDir")))
meld("Datei 1: "+v2c(
aDir
[1])
)
Fotofunktionen – nur für das MDE
Für das MDE gibt es die Möglichkeit Fotos von der Kamera aufzunehmen…
vRet= CaptureImage() | Diese Funktion ist speziell für die Geräte von Motorola (MC55) geschrieben und erlaubt es Fotos mit der Integrierten Kamera aufzunehmen. Die Funktion hat 11 Parameter 1. Wert: Name der zu erzeugenden Datei 2. Wert: Auflösung des Bildes zB. „R_1024x768“ (R_1024x768, R_800x600, R_640x480, R_1280x1024, R_160x120, R_320x240, R_352x288, R_176x144, R_1280x960, R_1600x1200, R_2048x1536) 3. Wert: JPEG-Komprimierung in % von 0-100 4. Wert: Fenstertitel 5. Wert: Infotext zum Bild 6. Wert: Gibt an, ob das Fotofenster geschlossen werden darf (rotes X) 7. Wert: Text für Button „aufnehmen“ 8. Wert: Text für Button „verwerfen“ 9. Wert: Text für „speichern und beenden“ 10. Wert: Text für „beenden ohne Bild“ 11. Wert: Text für „Lampe“ |
vRet=GetUpload() | Erzeugt die Datei aus dem Base64-Feld der Tabelle MDEUPLD (füllt AddUpload-Funktion) bzw. kopiert diese aus dem Upload-Verzeichnis an die gegebene Stelle. 1. Wert: Dateiname aus MDEUPLD 2. Wert: Ablagepfad 3. Wert: Soll Datei in der Upload-Tabelle erhalten bleiben 4. Wert: ??? 5. Wert: ??? Eventuell wird dies nicht mehr benötigt, da addUpload und die Datensynchronisation heute dafür sorgt, dass die Datei automatisch in einem Zielverzeichnis auf dem Server landet, (siehe Zugriff auf das Dateisystem) |
Ask(cFile,"Image", "IMGFile",232,130) | Erweiterung des ASK-Befehles um Bilder auf Masken darzustellen |
IMGFile.Image=cFile | Weist dem IMG-Objekt z.B. beim Klick ein neues Bild zu |
Beispiel:
am MDE
cFile=""
c
G
UIID
=""
lCloseButton=0
cNewFile=CaptureImage(cFile,"
R_
1024
x768
",75,"Kamera",cIMG_KKS+" - "+cIMG_Info,lCloseButton)
if
!
empty
(cNewFile)
cGUIID
=AddUpload(cNewFile,1)
* Name der Datei
cFileName=
allt
(cIMG_KKS)+"_"+
left
(
TTOC
(tIMG_TS,1),8)+"_"+
substr
(
TTOC
(tIMG_TS,1),9)
…UPLOAD…
E
ndif
Am Server
cGUIID
=vget(@cInput,"
GUIID
",,"C")
vRet=getupload(
cGUIID
,cZiel,lKeepDB,1,@cErrMsg)
Reports am MDE-PC
Mit dem vereinfachten Reportgenerator kann auch am MDE-PC ein Report gestaltet und erstellt werden:
layout("C:\ Rep .xml") | Startet den Reportgenerator mit der angegeben XML-Datei. Ist diese nicht vorhanden, so wird diese neu angelegt. |
PDF(oRep,cLayout,cFile) | Aufruf der PDF-Erzeugung. Parameter: Cursor für die Daten auf dem Report. Aktuell geht hier nur ein Cursor, der alle Daten pro Satz enthalten muss. Parameter: XML-Layoutfile für den Report oder der Einsprungspunkt, wenn der Report über die MDE-Verwaltung als Report gepflegt wird. Parameter. Zielname des fertigen Dokuments |
Run(cPDFFile ,,,.t. ) | Öffnet das angegeben Dokument auf dem MDE-PC Der 4. Parameter kann dazu genutzt werden, dass der aufrufende Dialog weiter bearbeitet werden kann (nicht modal). .f. ist der Default-Wert. |
Run(cPDFFile, "RK_KYO","printto") | Druckt das angegebene PDF-Dokument auf den angegebenen Drucker |
Beispiel für die Erstellung eines PDF-Dokuments
oReport=GetCursor(0,cFields,cFrom,cWhere,cOrder)
* Layoutfile für Report
cRepLayout=getcfg("W_REPSERVXML","C:\TEMP\RepService.xml")
* Datei, die erzeugt werden soll
cPDFFile=getcfg("W_REPFILE","C:\TEMP\RepService.pdf")
* Rufe Erzeugung auf
PDF(oReport,cRepLayout,cPDFFile)
Start externer Programme:
Um externe Programme aufzurufen existiert die RUN-Funktion:
Run(“x.exe”,”a=b”) | Die Funktion RUN startet das Programm, das als erster Parameter übergeben wird. Der zweite Parameter nimmt eventuell Übergabeparameter für das Programm auf. |
Beispiel für die Unterschriftenerfassung mit SignPad:
Run(„c:\Programme\StepOver\SimpleSigner.exe“, „c:\temp\sign.jpg
250 100 AutoSaveSig
-2
Textout-Unterschrift
“)
Beispiel für die Unterschriftenerfassung mit SignatureOffice:
run("C:\Program
me\StepOver\eSignatureOffice
\SOSigOffice.exe",cPDFFile+" -sss="+cTempSSS+" -scs="+cTempSCS+" -zoom=50"
Fremdsprachigkeit:
Prinzipiell sind die MDE-Programme auch dann mit einer Fremdsprachen-Oberfläche aufrufbar, sofern das Professional-ERP Modul „Fremdsprache“ eingesetzt wird.
In diesem Fall muss die Tabelle „LANGUAGE“ im SYS-Verzeichnis um folgende 2 Felder erweitert werden:
LAN_MDE | Numerisch 1 |
CURTIMEST | Datum / Zeit |
Dem Webservice muss mitgeteilt werden, wo er die Tabelle LANGAUGE finden kann.
SYSDB= E:\ProfERP\SYS
SYSCON=PROVIDER=SQLOLEDB;DRIVER=SQL Server;Address=SWSNBDG2016;SERVER=SWSNBDG2016;Database=PERPxxx_SYS;UID=sa;PWD
=xxx
Die entsprechenden MDE werden über die SWSMDE.INI und den Parameter MDELANG auf die Fremdsprache eingestellt.
MDELANG=E
Mit der Synchronisation werden vom Server alle Spracheinträge geholt, die das Kennzeichen LAN_MDE gesetzt haben.
Bei der Darstellung der Masken prüft das Programm intern, ob der entsprechende Spracheintrag für das anzuzeigende Objekt lokal vorhanden ist. Ist dieser nicht der Fall, so wird dies dem Server über die Job-Tabelle mitgeteilt. Dieser sucht nun in der LANGUAGE-Tabelle auf dem Server, ob er für das Objekt und die deutsche Übersetzung einen Eintrag findet. Ist dieser bereits angelegt, so wird nun das MDE-Flag gesetzt. Wenn nicht, wird ein neuer Eintrag mit Flag erzeugt. Daraufhin wird der Satz wieder auf das MDE synchronisiert.
Sofern für das Objekt kein englischer text eingetragen ist, wird der deutsche Text angezeigt.
Die Pflege der Übersetzungstexte erfolgt über die Firmenparameter – Extras - Übersetzungstexte. Hier kann über das Flag „MDE“ gefiltert werden.
cTest=„~“+v2c( n ) Meld(cTest) | Textstrings, die mit „~“ beginnen werden nicht übersetzt. Bei der Ausgabe wird die Tilde abgeschnitten. |
GetL(< D efault-Text>, <Objekt>,“C“) | Ermittelt den fremdsprachigen Text für ein Objekt mit dem vorgegebenen deutschen Text. |
Beispiele:
_SCREEN.Caption="~"
+_CR
+GetL("Service Auftrag","FRMMDE","C")
Anmerkungen:
Lokal wird die Tabelle LANGUAGE nur erzeugt, wenn ein Eintrag für MDELANG in der SWSMDE.ini gesetzt ist.
Die Login-Oberfläche startet, sofern ein Wert für MDELANG gesetzt ist, immer in englisch.
MDE-Jobverarbeitung
Möchte man auf eventuelle Probleme bei der Verarbeitung der MDE-Jobs reagieren, so steht hierfür die Datenregel _MDE zur Verfügung.
Im Einsprungspunkt *:VJOBUDATE hat man die Werte des aktuellen Jobs im Objekt goPData.MDEJOB.oFields.
Unterschiede in den Sprachversionen:
Die Unterschiede der Sprachversionen liegen vorwiegen im Zugriff auf die Datenbank. Da bei der PC-Version noch auf die klassischen FoxPro-Tabellen zu gegriffen wird, kommt am MDE die SQL-CE-Datenbank zum Einsatz.
Der Parameter _PC kann aber auch dazu verwendet werden um Funktionen, die am MDE nicht zur Verfügung stehen aus dem Ablauf und damit einem Absturz auszuklammern.
Anzeige <NULL> im Grid:
if _PC
cFields=cFields+",nvl(BE3_SNR,space(25)) as BE3_SNR"
else
cFields=cFields+",case when BE3_SNR is null then ' ' else BE3_SNR end as BE3_SNR"
endif
iif in Abfragen
if _PC
cFields="sum(iif(MPW_WERT>100,1,0))+0000 as PosWert"
else
cFields=”sum(case when [MPW_WERT]=1 then 1 else 0 end)+0000 as [PosWert]”
endif
Bit-Felder validieren
if _PC
cWhere=cWhere+" and bittest("+cMPZFeld+","+v2c(nBit)+")=1"
else
cWhere=cWhere+" and cast("+cMPZFeld+" as BGINT)&"+ v2c(nBit^2)+">0"
endif
Umstellung von Version 1.xxx auf Version 2.xxx
Neben dem immer erweiterten Sprachumfang gibt es im Bereich des Insert die größten Änderungen.
Die Funktion gibt nun als Rückgabewert die vom System lokal generierte ID zurück.
Gibt es zudem in der Tabelle das Feld MDEID (ohne jeden Präfix), so wird dieses Feld ebenfalls mit der ID gefüllt. Sollte dieses Synchronisations-Feld aus historischen Gründen anders heißen, so ist dieses manuell nach dem Insert Up-zu-daten.
Die ID verwendet die Konfigurationsvariable MDEID, die immer 3-stellig numerisch den jeweiligen MDE-Client eindeutig beschreiben sollte. Somit kann auf den bisherigen Offset verzichtet werden.
Die Funktion
NextID()
Alt:
nMDEOffset= val(alltrim(_MDEID))*1000000000
nPosID=val(getcfg("NEXTID","1"))
nPosMDEID=nPosID+nMDEOffset
INSERT
("POSITION","POS_AUF",nVORAUF,<…>,
"POS_MDEID",nPosMDEID
)
Neu – mit Feld MDEID:
nPosID= INSERT("POSITION","POS_AUF",nVORAUF,<…>)
Neu – mit historischem xxx_MDEID-Feld:
nPosID= INSERT("POSITION","POS_AUF",nVORAUF,<…>)
UPDATE("POSITION",nPosID,"POS_MDEID",nPosID)
Eingehend mit diesen Änderungen sind auch die _CHECK_CHANGE_ID Einsprungspunkte lokal und am Server anzupassen:
Aus
if
nAnsMDEID#0 and nAnsID=(nAnsMDEID-nMDEOffset)
wird
if
nAnsMDEID#0 and nAnsID=nAnsMDEID
Umstellung von Version 2.xxx auf Version 3.xxx
Folgende Einstellungen sollten für eine MDE (Honeywell) im klassischen gemacht werden, dass das Look and Feel gleich bleibt
ScaleFont=1,5 | Bringt die Schriftgröße in ein Format, das gut lesbar ist |
ScaleX=1,1 | Verbreitert die Maske so, dass es füllend auf dem Honeywell angezeigt wird |
ScaleY=1,2 | Skaliert die Höhe der Felder der Maske so, dass es füllend auf dem Honeywell angezeigt wird |
ScaleGrid=1,2 | Damit können die Spalten im Grid um einen Faktor vergrößert werden, so dass diese zum PC-Client passen. |
MENULOGO=NO | Entfernt auf der Menüebene das Professional-Logo, so dass 4 Menüpunkte auf die Seite des Honeywell passen |
PROGLOVE=NO | Hiermit kann der Button „Proglove verbinden“ auf der Login-Maske ausgeblendet werden |
LOGFILE=YES | Schafft die Möglichkeit Events auf dem Gerät zum Ablauf zu speichern. Die Datei mit dem Namen LogFile<datum> wird im LOG-Verzeichnis des ProfessionaERP-Ordners abgelegt |
LOGVERBOSITY=E | Gibt das Level für das Logging an (E=Error) Kurzform Beschreibung V Verbose D Debug I Info W Warnung E Error F Fataler Error S Still |
Kurze Infos zur Installation der APP:
Umstellung auf Dynamic-Mode
Anhand des Projektes PMT hier eine Auflistung der Arbeiten, die im Zuge einer Umstellung auf den Dynamic-Mode zu machen sind.
Es kommt dabei sicher auf den Zustand des Ausgangscodes an und die Features, die genutzt werden.
Die Liste ist sicher nicht vollständig, beschreibt aber die Probleme / Umstellungen, die in dem Projekt zu machen waren.
_INIT
Der Offset zur Prüfung der Datensynchronisation kann entfernt werden
nMDEOffset= val(alltrim(_MDEID))*1000000000
Für die Farben (show-Dialoge) sollte eine Sektion zur Definition der Farben eingeführt werden:
* Farben
nRGBHGMeld=rgb(184,206,225)
MASK
Speicherung der Maskendarstellung
Die individuellen Änderungen der Maske können im Dynamic-Mode ebenfalls in der Registry gespeichert werden. Dazu muss bei der Maskendefinition der Zweig mit angegeben werden:
Aus
Clear("Geräteverwaltung",860,698)
Wird nun
Clear("Geräteverwaltung",860,698,"frmSNRFFRM")
Menü vs. Ribbon
Die bisherigen Menüs führen im Dynamic-Mode zu einer Fehlermeldung und müssen daher entfernt werden
Alt:
AddBar("Datei","","barFile")
AddBar("Verwerfen","Undo","barUndo","barFile")
Neu:
* Ribbon-Bar
AddPage("padRecord","\<Datensatz")
AddGroup("padRecord","popAllg","Allgemein")
AddButton("popAllg","barUndo","Verwerfen","ESC
APE
","ESC","UNDO",4,1)
Die Namen für bar___ und die Funktonsaufrufe können einfach so übernommen werden.
Beachte: Die Ribbons werden durch Clear() nicht zerstört. Daher dürfen diese nur einmal erzeugt werden!
Headline über Reiter
Um das Aussehen an Professional-ERP anzupassen, sollte eine Headline unter der Ribbon-Bar eingefügt werden. Die Befüllung erfolgt dann üblicherweise im READ der Funktion
* Headline
nAskMASK=AskTop()
Ask("","cHeadInfo","TXTHEADLINE",,,4)
TXTHEADLINE.FontBold=.t.
TXTHEADLINE.FontSize=15
TXTHEADLINE.ForeColor=rgb(52,97,140)
TXTHEADLINE.WResize=.t.
Ask()
Ask()
Ask()
Basis-Container - Reiter oder nur einen Container einfügen
Wird mit einer Ribbon-Bar gearbeitet, so ist immer mindestens ein größenveränderlicher Reiter einzufügen:
* Basis-Container als Reiter
Ask("","Main","PGF",,492)
pgfMain.WResize=.t.
pgfMain.HResize=.t.
Ohne Ribbon-Bar reicht ein Größenveränderlicher Container
* Container zur Anzeige der ToDo
Ask("","AdrUms","CNTSUCH",,444)
CNTSUCH.WResize=.t.
CNTSUCH.HResize=.t.
Objekte größenveränderlich gestalten
Alle Objekte können nun auf das Vergrößern und Verkleinern der Maske eingestellt werden. Hier eine Auswahl der Befehle:
<… >.WResize=.t. | Vergrößert das Objekt in der Breite |
<… >.HResize=.t. | Vergrößert das Objekt in der Höhe |
<… >.HCount=3 | Gibt an wie viele größenveränderliche Objekte in der Horizontalen vergrößerbar sind (Achtung nicht Höhe sondern in der Breite) – muss bei jedem Objekt angegeben werden |
<… >.HPos=3 | Gibt die Position an, an der wievielten Stelle das Objekt in Bezug auf die horizontalen größenveränderlichen Objekte liegt. |
<… >.VCount=2 | Gibt an wie viele größenveränderliche Objekte in der Vertikalnen vergrößerbar sind |
<… >.VHPos=2 | Gibt die Position an, an der wievielten Stelle das Objekt in Bezug auf die vertikalen größenveränderlichen Objekte liegt. |
Meldungen:
Die Meldungen, die dem User mehr als eine Einfache Info geben sollen, sollten von einem
Meld()
Show()
Aus
meld("Kein Termin gefunden")
wird
show
(_CR+"Kein Termin gefunden!","Info",1,nRGBHGMeld,nRGBGruen,16)
Der Vorteil des Show ist, dass dieses auch im Bereich der Validierungen <VA_xxx> eingesetzt werden kann, da hier das Bestätigen der Meldung nicht erneut zu einem Focus-Erhalt in dem Feld führt, was im Normalfall dazu führt, dass das Valid erneut schießt. Endlosschleife
Analog wurde die
J_N_A()
-
Aus
nJNA=j_n_a("Servicezeit übernehmen?","J","Service Auftrag")
kann folgendes werden
nJNA=j_n_a("Es sind noch..."+_CR+cAnzText+_CR+"Trotzdem weiter?“
,"N","Info",1,nRGBGruen,nRGBRot,nRGBBlau,12)
Sonstige Änderungen:
Shortcuts auf Buttons, Reiter etc. sind von der bisherigen Schreibform
„&Speichern“
„\<Speichern
Die bisherige Funktion
<…>.SetFocus()
<…>.Focus()
Bei Comboboxen steht nur die neue Eigenschaft
<cboxxxx>.ShowValue=.f.
Die alte Version konnte den Exakt-Vergleich auch auf numerische Werte verarbeiten. Dies ist in der neuen Version nicht mehr zulässig. Also nach „==“ suchen und ggf. ersetzten.
Der Serverprozess
Die Abarbeitung der Aufgaben von den verschiedenen MDE erfolgt über einen Serverdienst. Dieser wird in den Diensten über den Menüpunkt „MDE Job-Verarbeitung“ gestartet.
In dem angezeigten Meldungsfenster werden alle verarbeiteten Aufgaben protokolliert und eventuelle Fehler angezeigt.
Parallel zur on Screen Dokumentation werden die abgearbeiteten Aufgaben in der Datei MDEPROT.TXT im Weekend-Verzeichnis aufgezeichnet.
Beispiel der Protokollierung
05.04.2011 08:04:22.03 Verarbeitung gestartet [PC08 # Maier]
UTC 05.04.2011 06:06:05 123 WFPSELECT(SERVANNEHM)
Programmstarte auf Desktop legen
Man kann den Programmteil per Rechtsklick auf den Desktop legen.
Möchte man zudem eine Startmöglichkeit ohne User- und Passworteingabe ermöglichen, so kann der Zieleintrag im Icon um den Parameter
/v<USER>$<PASSWORT>
Beispiel:
C:\xxx\PROFERP.EXE -t -c"C:\xxx\CONFIG.FP" .t. $ID998ID$ MDE_FRM$with$'MDE_JOB',,'*,KO' "C:\xxx\WEEKEND.DAT\"
/vmde§MDE
3721
Der MDE-Systemstatus
Einen Überblick über die Jobverarbeitung und die angeschlossenen Geräte erhält man über die Maske „MDE-Systemstatus“.
Die Schalter zum Beeinflussen der Aktualisierungsfrequenz lauten:
MDEACTIVE_C für MDE-Clients
MDEACTIVE_T Topas-Server
MDEACTIVE_* für restliche Geräte
MDEJOBACTIVE Prüfung ob Jobverarbeitung noch aktiv (eventuell MDEACTIVE_V?)
Die SWSWeb.ini
Damit der Webservice korrekt mit der ProfessionalERP-Datenbank zusammenarbeiten kann, müssen hierzu grundlegende Einstellungen in der SWSWeb.ini hinterlegt werden.
Beispiel:
WEBUSER=PROFESS
WEBPW=SWS
DB=E:\WEEK60\weekend.dat
PROT=C:\TEMP\swsweb.txt
WEBUSER=PROFESS | Name des erwarteten Users vom MDE aus dessen Ini-Datei. Dieser muss passen, sonst wird der Zugriff verweigert. |
WEBPW=SWS | Passwort des registrierten Users vom MDE. Dieser muss passen, sonst wird der Zugriff verweigert. |
DB=___ | Pfad zur Professional-ERP – Datenbank. Als Laufwerk oder als UNC-Pfad |
CON=___ | SQL bei SQL-Datenbank. Benötigt DB=___ nicht mehr *CON=PROVIDER=SQLOLEDB;DRIVER=SQL Server;Address=<<IP,Port>>;SERVER=<<IP>>;Database=<<DB-Name>>;UID=<<DB-User>>;PWD=<<DB-PW>> |
Prot=___ | Dieser Schaltet die SQL-Protokollierung ein. Hiermit können Fehler in der Synchronisation festgestellt bzw. die Statements bei der Verarbeitung überprüft werden. |
PROTMDE=x,y | Protokollierung nur für die angegebenen MDE |
IDPRAEFIX=1 | Steuert die ID-Vergabe in der MDEJOB je nach Webservice über den der Job ankommt. |
JOBCHECK=YES | Prüft ob JOBS mit identischem Inhalt im Feld MDJ_PARAM, ToDo-Art und Gerät schon da ist. Wenn ja, so wird der zweite nicht beachtet. Default ist YES |
UPLOADPATH=E:\... | Verzeichnis in welches die addUpload(cFile) bei der Jobverarbeitung gespeichert werden |
HASH=NO | Ab der Version 2.5. wird bei der Anfrage an den Webservice vom Client immer ein Overhead plus einem Hash-Wert mit übergeben. Der Webservice nimmt somit nur noch Datenanfragen entgegen, wenn der Hashwert passt. Dies dient hauptsächlich zur Sicherheit für unbefugten Datenzugriff von außen. Das heißt aber auch, dass man den Webservice bzgl. GetCursor() nicht mehr testen kann, wenn der Schalter nicht gesetzt ist. Default ist YES |
Auch der Webservice kann von einem Server auf den anderen Server weitergeleitet werden. In dem Fall muss in der INI des ersten Webservice nur die Adresse des zweiten angegeben werden:
WEBSE
R
VICE=http:
//
webserv2
/
SWSWeb
/
SWSWebService.asmx
Installationsprobleme
Kommt bei der Synchronisation der Fehler, dass in der MDETABLE das Feld CURTIMEST fehlt (Fehler beim Insert), dann ist der DATA_TYP beim GETSTRUCT im Webservice falsch.
Ursache ist die fehlende oder falsche Version des OLEDB \MDAC\OLDEDBVFP9…
GetStruct muss bei DateTime Feldern den DATA_TYP = 135 zurückliefern
Fallen in der Programmierung SQL
OR-Abfragen
Die folgende Abfrage mit der oder-Verknüpfung benötigte über 2100 ms obwohl beide Felder einen Index hatten
select ID from ARTIKEL where ART_ARTNR='Z1033 ' or ART_EAN='Z1033 '
Aufgebohrt in zwei Abfragen
select ART_ARTNR from ARTIKEL where ART_EAN='403884702184 '
und danach
select ID from ARTIKEL where ART_ARTNR='Z1033 '
ergab jeweils eine Zeitdauer von 40 ms.
Programmhistorie
Im Laufe der Zeit wurde das Modul immer weiter ausgebaut und um neue Funktionen erweitert. Hier die Eckpfeiler der unterschiedlichen Versionen.
Version | |
1.0. | Alle Grundfunktionen für MDE und Laptop Synchronisation und Serverprozesse |
1.1. | Verbesserte Synchronisation (Abbrüche und Rücksetzung) Gekoppelte Webservices |
1.2. | Objektnamen und Fremdsprachigkeit MDT_TABLE auf Memofeld umgestellt um Tabellenjoins zu ermöglichen. |
1.3. | Einsprungspunkt _EXIT beim Verlassen Maske Release(„nWert“,“cString“, ….) eingebaut Empty(dWert) für Datumswerte Checkbox korrigiert |
1.4. | Abs() VA_ und WH_ für CBK, CBT und CHK bei Änderung Wert Sortierung in CBK nach WAH_TEXT Time-Funktion |
1.5. | Coverage-Funktion – Protokolliert die Dauern der einzelnen Zeiten auf dem MDE und protokolliert diese in die angegebene Datei. INI: COVERGAE=My Documents\coverage.txt |
1.6. | Ttod() - Wandelt Zeit in ein Datumsformat um |
1.7. | Verbesserte Synchronisation der Jobs bei Verbindungsabbrüchen.Download neuer Softwarestände durch Doppel auf Logo der Software-Schmiede. SWS.CAB-File muss im Verzeichnis des Webservices liegen. |
1.8. | Synchronisations-Bedingungen für die Geräte (MDT_MDE und MDF-MDE) werden nun unterstützt. |
1.9. | _WHEN und _VISIBLE als globale Einsprungspunkte eingeführt. (varams2 enthält nun den Wert für die übergeordnete Einheit z.B. den Container) Alle Objekte haben nun die Eigenschaft .FontSize |
1.10. | Checkbox und Combobox werden nun auch über WHEN gesperrt. |
1.11. | Synchronisation über mehrere Tabellen bei gejointen Abfragen |
1.12. | EDT-Felder Zeilenumbruch mit Eingabetaste Sichtbarkeit der Reiter (nicht machbar) aber die Aktivierung wird unterdrückt. SyncStop und SyncStart überarbeitet Über cBarText kann im VI_ der Text der Menüs geändert werden. Arrays nun verfügbar. |
1.13. | Masken schließbar per Programm: Close() Funktion der aufrufenden Maske ausführbar: ParentDo("SEARCH") Fremdsprachigkeit Klick ins Grid an allen Stellen ermöglicht die Verarbeitung der Zeile |
1.14. | |
1.15. | goMonth |
1.16. | Beim Löschen von Sätzen durch die Synchronisation am PC wird die ID des Satzes zuerst fortlaufend negativ belegt. Wert wird in der INI gespeichert (Verletzung Unique Key) Der Klick ins leere Grid führt zu keinem Absturz mehr |
1.17. | Korrektur des Filters für MDF_BED, damit alle Funktionen wieder sichtbar sind nach Import. Neuer Befehl zur Bereinigung von Arrays. CBOxxx_xxx.Clear() Mehrstufige SWSMDEINI durch den Eintrag SWSMDEINI=<neuer Ort> |
1.19. | Verteilte SWSMDEINI auch für Laptop |
1.20. | OK-Button auskommentiert auf den MDE-Masken |
1.24 | SUBV – alle Felder des Users |
1.25 | Neues Logo, at(), atc(), bar<xxx>.text und –enabled, PKEY_<xxx> und logout() |
1.3.10 | DelRow, KeyStop und KeyResume |
1.3.11 | INI-Werte: MDEDEL und RECDEL |
1.3.14 | Synchrolücke geschlossen: Sofortiger Abbruch der Synchronisation, wenn neuer unverarbeiteter Job vorliegt |
1.4.00 | Zugirff auf WebServer mit Komprimierung (abschaltbar durch WEBCOMP=NO) |
1.4.01 | verarbeitete Jobs aus Tabelle MDEJOB löschen / abschaltbar durch DELJOBS=NO |
1.4.02 | Syncabbruch, wenn der erste Job nicht an den Webservice übergeben werden konnte (Überholen von Jobs) |
1.4.03 | Einsprungspunkte _LOGOUT/_EXIT in _MENU Bei erstem Sync keine Sätze für MDEJOB abfragen |
1.4.04 | Einsprungspunkte _INIT und VI_CMDxxxx für Hauptmenü auch bei erneuter Anmeldung aufrufen Schalter DOPROT zur Protokollierung der aufgerufenen Funktionen Text „keine Verbindung“ wenn keine Verbindung zur Datenbank Bei Abbruch Sync: Meldung Syncabbruch Tabelle mit gelbem Hintergrund Anzeige WebService und Datenbank bei Doppelklick auf Versions-Container Server: Try/catch bei aktiver Protokollierung auf WebServer Default von JobCheck=YES Sonderzeichen vor Übergabe ausfiltern |
1.4.05 | Anzeige Zeitpunkt letzte Synchronisation bei „keine Verbindung“ Variablen _LASTSYNC und _LASTSYNCUTC Funktionen ttoc, dtoc |
Empfehlungen
Parameter in MDE-Jobs
Vom Client werden an den Server Jobs in der Tabelle MDEJOB übergeben. Im Feld MDJ_PARAM stehen hier im Klartext die Übergabeparameter anhand denen der Serverjob die Abarbeitung vornimmt.
Dies sieht für das Befüllen von Boxen im Kommissionier-Rundgang in etwa wie folgt aussehen:
BOOKTYPE=I
BOXID=100161018001
BOXMDEID=100161018001
VORID=86501000129568
LAPID=86501000000929
LAPNAME=BOX-006
PICKER=SUPERUSER
KOBRDAT=18.10.2016 14:23:25
KOBSTATUS=1
USER=SUPERUSER
MDEID=100
_UTC={^2016-10-18 12:23:25}
_USER=SUPERUSER
Diese Informationen sind für den Server-Prozess ausreichend. Für ein einfacheres Nachvollziehen oder für einen einfacheren Support wäre es sehr hilfreich, wenn man z.B. neben der VORID auch noch die Vorgangsnummer mitzugeben. Im Beispiel oben wurde so schon der Lagerplatzname für die Box schon mitgegeben.
Dadurch würde bei der Untersuchung der MDE-Jobs einfacher klar welche Buchungen zu welchem Vorgang gehören.
Hilfsfunktionen zur Analyse
Für die Fehlersuche in einer bestimmten MDE-Anwendung ergeben sich mit der Zeit immer wieder dieselben Fragestellungen. Z.B. bei einer MDE-Lagerlösung: Alle MDE-Jobs zu einer Auftragsnummer.
Hier empfiehlt es sich Hilfsfunktionen oder Auswertungen zu schreiben, mit denen diese Fragen beantwortet werden können ohne ständig im DBC komplexe Abfragen schreiben zu müssen.
Granularität von MDE-Jobs
Bei der Abarbeitung von MDE-Serverjobs sollte bei der Entwicklung auf eine passende Granularität geachtet werden. Es kann sinnvoll sein, einen MDE-Serverjob in mehrere kleinere Jobs auf zu spalten.
Beispiel: Aufspaltung eines Jobs der einen Lieferschein drucken soll.
Hier könnte es sinnvoll sein, diesen Job serverseitig zu teilen in den Wechsel des Vorgangsstatus auf Lieferschein und in einen Job der nur den Lieferscheindruck vornimmt.
Potentielle Vorteile:
Der Code ist übersichtlicher und damit wartungsfreundlicher, da der allgemeinen Programmierempfehlung gefolgt wird das eine Funktion nur eine Verantwortlichkeit hat.
Besseres Fehlerhandling. Wenn z.B. nur der Druckjob fehlschlägt weil der Drucker nicht bereit ist, kann dieser Job einfacher wiederholt werden.
Eventuell bessere Performance: Falls Prozessbeteiligte nur Teilergebnisse brauchen, können diese schneller weiterarbeiten da die anderen Teilergebnisse in anderen MDE-Jobs abgearbeitet werden.
Je nach Anwendung ist es sogar denkbar, mehrere MDE-Jobverarbeitungen zu nutzen – z.B. eine zusätzliche MDE-Jobverarbeitung, die nur Druckjobs abarbeitet.
Fehlerbehandlung und Stabilität
MDE-Lösungen werden in einem ganz anderen Kontext ausgeführt als die Professional ERP Anwendung (Asynchronität der MDE-Clients, Kommunikation über Netzwerke, Job-Abarbeitung ohne menschlichen Nutzer) und haben damit das Potential zu lange Zeit unbemerkten und sehr komplexen Fehlerbildern. Dies muss in der Programmierung beachtet werden. Es ist aber auch unvermeidlich organisatorische Maßnahmen zur Überwachung der MDE-Lösung und Fehlerbehandlung zu treffen. Dazu gilt:
1. Es muss sichergestellt werden, dass eine verantwortliche Stelle beim Kunden existiert, die das Laufen des Webservice und der Jobverarbeitung überwacht. Hierzu muss es zwingend Dokumentation geben, damit Support durch Mitarbeiter der SWS geleistet werden kann, die den Kunden nicht kennen.
2. Es müssen in der MDE-Lösung bereits Mechanismus zum Abfangen von Fehlern eingebaut werden. Falls die Fehler nicht in der Anwendung selbst behandelt werden können, muss ein Verantwortlicher informiert werden. Es ist fatal wenn die Jobverarbeitung zwar stabil läuft, aber ständig Fehler produziert die niemand mitbekommt. Am wichtigsten ist fehlerhaft verarbeitete MDE-Jobs an einen Verantwortlichen zu melden. Hier haben sich automatisch generierte Todos oder Emails bewährt.
Es gilt zu beachten, dass fehlerhaft verarbeitete MDE-Jobs jederzeit auftreten können, auch bei MDE-Lösungen die bereits seit Jahren fehlerfrei laufen. Grund dafür können nicht nur Programmierfehler oder nicht beachtete Sonderfälle sein, sondern auch ausgefallene Infrastruktur oder Änderungen im Professional-ERP Umfeld.
Beispiel: Fällt die Verbindung zum Archivsystem aus, können MDE-Jobs fehlschlagen die an sich korrekt programmiert sind. Es muss dann auch zwingend ein Verantwortlicher informiert werden, da die Verbindung zu Docuware i.d.R. nur durch einen menschlichen Eingriff wiederhergestellt werden kann.
Es ist nicht notwendig und auch unmöglich für jeden möglichen Fehler eine vorbereitete Lösung zu haben, es ist aber im Vorfeld organisatorisch zu klären, wie aufgetretene Fehler entdeckt, klassifiziert, bewertet und behandelt werden können. Dies geht nicht ohne einen Verantwortlichen beim Kunden, dem aufgetretene Fehler automatisch gemeldet werden bzw. der das Laufen der Automatismen überwacht.
Dieser Verantwortliche muss auch entscheiden können wie Fehler behandelt werden. Denkbar sind zum Beispiel folgende Behandlungsebenen:
1. Ignorieren, da Job nicht wichtig genug ist. Beispiel: Interner Versand einer Infomail schlug fehl weil der Mailserver vorübergehend nicht erreichbar war.
2. Beseitigung von Problemen und Wiederholung von Jobs (über ein Tool oder mit Hilfe der SWS)
3. Korrekturmaßnahmen, z.B. Lagerumbuchungen oder manuelles Erfassen von den Informationen die der MDE-Job in die Datenbank schreiben sollte
4. Eskalation an den Support der Software-Schmiede zur Untersuchung und Beseitigung der Fehlerursachen
Offensichtlich ist dies alles abhängig von der jeweiligen Lösung sowie der Auswirkung möglicher Fehler beim Kunden. Mehr Sicherheit verursacht auch immer mehr Kosten. Es kann aber zu fatalen Folgen führen, wenn keinerlei Strategie für die Überwachung und die Behandlung von Fehlern existiert.
Für weitere Infos zu Professional ERP steht Ihnen das Team der Software-Schmiede Vogler & Hauke GmbH gerne zur Verfügung
Besuchen Sie uns doch mal im Internet!
Auf dieser Seite enthalten:
Weitere hilfreiche Seiten:
Filter by label
There are no items with the selected labels at this time.