Mein Name ist Ulrich Fuchs, ich unterstütze als freier Berater Anwenderunternehmen und Beratungshäuser in allem, was im Umgang mit dem ERP-System "Infor LN" und seinen Vorgängerversionen (Baan IV, Baan V) so an Aufgaben anfällt. Ich bin Projektleiter, Berater und Softwareentwickler, und weil ich alle drei Rollen tatsächlich beherrsche, bin ich der Joker immer dann, wenn's anspruchsvoll wird.

Weil Neuigkeiten, über die ich so stolpere, vielleicht auch für andere interessant sind, gibt's dieses Blog. Meine Kontaktdaten finden Sie unter
www.ulrich-fuchs.de.



Mittwoch, 13. August 2008

Nicht tun, bitte: Nummer 1) Nicht kommentieren

Man sollte es nicht glauben, aber das ist noch immer die häufigste und schlimmste Sünde. Schon bei der Eigenentwicklung von Komponenten, noch schlimmer bei der Anpassung von Standardkomponenten. Da könnte Lagnese glatt ein achtes Todsündeneis erfinden.

Grundsätzlich gilt bei der Anpassung von Standardkomponenten: Es wird kein Code gelöscht, sondern nur auskommentiert. Jedes eingefügte Codesegment wird als solches gekennzeichnet.

Oben im Kopf des Programmskriptes wird unterhalb der Standardkommentare angegeben, dass man eine Änderung vorgenommen hat, wann das war, was man gemacht hat, und warum. Für diese Änderung wird ein bestimmtes Kürzel angeben, mit dem die Änderungen im Programmskript gekennzeichnet sind. Für die nächste Änderung (sei es, aufgrund neuer Anforderungen, oder weil man zwei Tage später noch einen Fehler gefunden hat, wird ein neuer Kommentar im Kopf eröffnet und ein neues Kürzel vergeben.

Wie diese Kürzel auszusehen haben, dafür hat sich leider noch keine Konvention durchgesetzt. Infor nutzt hier Solution- oder Businessrequestnummern. Hat die eigene EDV ein ähnliches Prinzip, dass jede Softwareänderung in Bezug auf einen Eintrag in einem Change-Managementsystem stehen muss, kann man es ähnlich halten. Vorausgesetzt, man kann da tatsächlich mehrere Nummern generieren, wenn man mehrfach wegen einer Anforderung eine Source ändert. Ich persönlich bevorzuge es, für diesen Zweck meine Initialen und eine dreistellige Zählnummer zu nutzen (also: UF001, UF002, UF003 etc.) Nicht sehr praktikabel dagegen finde ich die Methode, mit Initialien und Datum zu arbeiten (UF080813), das wird zu lang, und bei zwei Änderungen an einem Tag hat man ein Problem.

Wofür es aber eine Konvention gibt, ist die Art, wie man dieses Kürzel zum Markieren seiner Änderungen im Skript benutzt. Diese Konvention wurde seinerzeit unter Baan eingeführt, wird bis heute zur Markierung von Änderungen aufgrund Solutions genutzt und sollte eigentlich allgemeiner Standard sein. Leider halten sich nicht alle dran, darum hier mal in aller Ausführlichkeit: Gesetzt den Fall, wir haben für unsere Änderung das Kürzel UF001 definiert. Dann kennzeichnet:

  • UF001.o eine herausgelöschte, (das heißt auskommentierte!) Proogrammzeile. Das o steht für "out".
  • UF001.n eine neue Programmzeile. Ein n für "new" also
  • UF001.so den Beginn einer größeren Anzahl von Zeilen, die man herausgenommen hat (so für "start out")
  • UF001.eo das Ende eines solchen Blocks (end out)
  • UF001.sn den Beginn einer größeren Anzahl von Zeilen, die man eingefügt hat (sn für "start new")
  • UF001.en das Ende eines solchen Blocks (end out)
Als Faustregel gilt, dass mehr als zwei eingefügte oder gelöschte Zeilen mit so/eo und sn/en markiert werden. Unlesbar werden Programme, wenn jede einzelne neue Codezeile mit einem solchen Marker versehen wird.

Möchte man also in folgendem Code...



|*
|* This would be the last part of the Infor standard comment
|* section on top of the script
|*
|*************************************************************

...
select tcibd001.*
from tcibd001
where tcibd001._index1 inrange {:item.f} and {:item.t}
and tcibd001.csel >= {:csel.f}
and tcibd001.csel <= {:csel.t}
and tcibd001.citg >= {:citg.f}
and tcibd001.citg <= {:citg.t}
order by tcibd001._index1
selectdo
call.a.funny.method (tcibd001.item)
endselect

die Selektion ändern und den Funktionaufruf durch einen andere Funktion ersetzen, so modifiziert man das bestehende Programm wie folgt:

|*
|* This would be the last part of the Infor standard comment
|* section on top of the script
|*

|*************************************************************
|*
|* UF001 - 2008-08-13,
|* Ulrich Fuchs, Büro für Informationsarchitekturen, mail@ulrich-fuchs.de
|* * Zusätzliche Artikelselektion statt über Selektionscode
|* und Artikelgruppe über Signalcode
|* * eine andere Lustige Funktion gerufen
|*
|*************************************************************

...
select tcibd001.*
from tcibd001
where tcibd001._index1 inrange {:item.f} and {:item.t}
|UF001.so
| and tcibd001.csel >= {:csel.f}
| and tcibd001.csel <= {:csel.t}
| and tcibd001.citg >= {:citg.f}
| and tcibd001.citg <= {:citg.t}
|UF001.so
|UF001.sn
and tcibd001.csig >= {:csig.f}
and tcibd001.csig <= {:csig.t}
|UF001.en
order by tcibd001._index1
selectdo
| call.a.funny.method (tcibd001.item) | UF001.o
call.another.funny.method (tcibd001.item) | UF001.n
endselect

Keinesfalls aber sollte man also geänderte Zeilen finden, bei denen weder klar ist, was vorher da stand, noch was geändert wurde. Mancher Programmierer und dito manche -in greift in obiger Situation schon mal zu

...
selectdo
call.another.funny.method (tcibd001.item) | UF001
endselect

Das ist böse, gemein, unwartbar. Nicht tun, bitte.

Nicht tun, bitte

Eines der häufigsten Argumente, warum man Baan-Software nicht anpassen soll, ist, dass die Anpassungen ja so schlecht wartbar seien und es immer so aufwändig sei, diese auf neue Solutionstände zu migrieren. Mittlerweile glaubt das sogar Infor.

Es stimmt einfach nicht.

Wenn Anpassungen schwer zu migrieren sind, dann liegt es in der Regel daran, dass sie schlecht spezifiziert und leider meist noch schlechter programmiert sind.

Ich werde mal versuchen, die häufigsten Programmierfehler (aus meiner Sicht) aufzulisten, die mir in diversen Anpassungen auf diversen Baan-Systemen begegnen, mit denen ich so zu tun kriege. Nicht, um mit dem Finger auf jemand zu zeigen (Namen werden eh keine genannt), sondern damit gerade Programmierneulinge in den Baan-Tools nicht nur von schlechten Beispielen lernen.

Jeder "Fehlertyp" (englisch heißt sowas "Pitfall") kriegt ein eigenes Posting, hier führe ich das ganze dann immer zusammen.

Nun denn:

#1 - Nicht kommentieren
#2 - Globale Variablen, ohne dass das sein muss
#3 - Nichtssagende Variablennamen

Dienstag, 1. Juli 2008

Finanzierung von Infor-Installationen über IBM

Trading Markets berichtet, dass Infor mit IBM eine Partnerschaft eingegangen ist, mit deren Hilfe Infor-Kunden über die IBM Global Financing entsprechende Installationen finanzieren können.

Sonntag, 22. Juni 2008

Johann und Peter

Ja ich weiß, heute ist Sonntag. Aber ich arbeite auch nicht. Trotzdem: Wer hätte gedacht, dass es unser Jan und Paul noch so weit bringen würden, zu mythischen Figuren zu werden, über die man Filme dreht?

Nun ist De uitverkorene (Die Auserkorenen) schon zwei Jahre alt, und ich hab's jetzt erst mitgekriegt.

Die Handlung und die Personen sind frei erfunden... Oder so...

Mittwoch, 18. Juni 2008

User Exits jetzt verfügbar

Sie sind ja schon Helden der Kommunikation, unsere Inforianer (zumindest die Jungs und Mädels von der Baan-Schiene). Heimlich still und leise haben sie die schönste Neuerung seit den "secondary main tables" (was bitte? Ich weiß, die kennt auch noch keiner. Ich komm drauf zurück....) - die schönste Neuerung seit den "secondary main tables" also, in die Systemschicht geschmuggelt. Und keinem was gesagt.

Die Neuerung heißt "User-Exit", und hat nichts damit zu tun, was mancher Helpdeskmensch manchem Anwender wünscht.

User-Exists sind Ausgänge für Anwender im Sinne eines Baan einsetzenden Unternehmens. Sie bieten die Möglichkeit, eigenen Programmcode an bestimmte Ereignisse im normalen CRUD-(Create-Read-Update-Delete)-Ablauf einer Session dranzuhängen. Und zwar, das ist der Gag dabei, ohne Standardskripte anpassen zu müssen. Also so ziemlich die Funktionalität, auf die man in der Baan-Entwicklung schon seit 1975 (oder so) wartet.

Das ganze funktioniert allerdings nur unter ERP LN. Soweit ich rausgekriegt habe, ist die Tools-Version (nicht das Porting-Set, nicht der Featurepack, nicht die Tools-Interface-Version) ausschlaggebend. Ist die Tools-Version 7.6.b4 installiert, sind die User-Exits lauffähig. Für die Tools-Version 7.6.a4 brauchts wohl die Solution 224004.

Mit dem User-Exit kann man Programmcode schreiben, der immer dann aufgerufen wird, wenn Datensätze in eine Tabelle eingefügt werden, geändert werden oder gelöscht werden. Vorausgesetzt, die Programme, die das tun, nutzen die DAL-Methoden (dal.save etc.), oder eine solche Änderung passiert auf der Maintable einer normalen Session.

Das ganze hört sich sehr nach DAL an und funktioniert auch ähnlich. Der Trick ist die Namenskonvention. Man definiert ein Programmskript, das genauso heißt wie die Tabelle, für die es gelten soll, hängt aber die Buchstaben "ue" für User-Exit hinten dran. Also: Das User-Exit-Skript für den Artikelstamm heißt tcibd001ue.

Beim Einfügen eines neuen UE-Skripts ist das System so clever, den Typ automatisch auf "Bibliothek" (DLL) zu stellen und ein Rumpfprogramm zu erzeugen.

Oben im Skript einzufügen ist ein #include <bic_dam>

Im Skript kann man dann bis zu acht Funktionen definieren, deren Namen schonmal zu Hirnverknotung führen können:

  • ue.before.before.save.object([long mode])
  • ue.after.before.save.object([long mode])
  • ue.before.after.save.object([long mode])
  • ue.after.after.save.object([long mode])
  • ue.before.before.destroy.object()
  • ue.after.before.destroy.object()
  • ue.before.after.destroy.object()
  • ue.after.after.destroy.object()

Diese werden, sofern vorhanden, jeweils vor bzw. nach der enstprechenden Funktion des DALs aufgerufen (ue.after.before.save.object () also bspw., nachdem die Funktion before.save.object() des DALs gerufen wurde). Und in diesen Funktionen hat man dann die ganze Bandbreite zur Verfügung wie im DAL, soll heißen, alle Felder der entsprechenden Tabelle sind hübsch gefüllt; with.old.object.values.do(...) und with.object.set.do (...) funktionieren, etc. Gibt eine Funktion 0 zurück, ist alles prima gelaufen, will man die Aktion abbrechen, kann man wie üblich mit dal.set.error.message (...) eine Meldung setzen und den Fehler mit dem Rückgabewert DALHOOKERROR den Call-Stack nach unten durchreichen.

Bevor man also als LN-Anwender überlegt, das xte DAL aufzubohren oder die xte Session anzupassen, um die üblichen Anforderungen ("wer hat diesen Datensatz wann geändert" etc.) zu erfüllen, sollte man ein Update von Tools und ggf. Portingset schonmal in Betracht ziehen. Die User-Exits erlauben zwar ein Einklinken nur auf Datensatzebene (für zusätzliche Feld-Checks etc. muss man immer noch das DAL der Tabelle aufbohren), aber das hilft häufig schon weiter.

Kleines Beispiel gefällig?




|******************************************************************************
|* tcibd001ue
|* User Exit Item Data
|******************************************************************************

#include <bic_dal>
table ttcibd001
table txyzus001


function extern long ue.after.after.save.object(long mode)
{
| Zum Beispiel: Bei Neuanlage oder Änderung User in Tabelle xyzus001 abstellen


on case mode
case DAL_NEW:
xyzus001.item = tcibd001.item
xyzus001.logn = logname$
db.insert (txyzus001, db.retry)
break
case DAL_UPDATE:
select xyzus001.*
from xyzus001 for update
where xyzus001._index1 = {:tcibd00._item}
selectdo
xyzus001.logn= logname$
db.update (txyzus001, db.retry)
endselect
endcase
return(0)
}


Letzter Tip: Damit User-Exits für die Tabelle tfacr200 möglich werden, braucht's die Solution 229257 nebst einer dort beschriebenen Parametrierung in tcmcs095 (weil die Standardskripte sonst offenbar "dumme" db.inserts machen). Vermutlich haben anderen Tabellen ähnliche Probleme, also nicht wundern, wenn's noch hakelt. Die Developer von Infor zaubern in letzter Zeit ein bisschen, aber auch Zaubereien schüttelt man nicht aus dem Ärmel.

Montag, 16. Juni 2008

Bauchladenschaufenster

Der Infor-Seite http://www.infor.com kann man ja leider nicht so richtig entnehmen, was sich Infor so alles zusammengekauft hat. Schön übersichtlich aufbereitet hat das ganze das Enterprise Software Directory hier.

Samstag, 14. Juni 2008

Wozu Testfirmen gut sein können

Testfirmen sind nicht zu unterschätzen. Nicht nur zum Testen, auch als Backup. Geben wir's zu: Jeder, der aus irgendwelchen Gründen Massenupdates auf einem Produktivsystem fahren muss, zerschießt mal Daten.

Ein Kunde wollte global Arbeitspläne aktualisieren. Durch einen dummen Fehler waren auf einmal in über 15000 Arbeitsgangzeilen die Vorgabezeiten futsch. Mindestens vier Leute, mich eingeschlossen, haben alle zusammen ein bisschen gepennt und eine Nebenwirkung eines dafür leicht angepassten Standardprogramms im Zusammenspiel mit der Art, wie die Stammdaten beim Kunden eingerichtet sind, einfach übersehen. Getestet haben wir das Updateprogramm dann auch noch ausgerechnet mit Daten, bei denen das Problem systembedingt nicht auftrat. Wie in solchen Fällen üblich, addierten sich also eine Menge kleinerer Unaufmerksamkeiten zum Unfall. Sollte nicht passieren, ist aber passiert. Gemerkt hat man das drei Tage später - zu spät, um die Tabelle aus einem Backup zurück zu laden, weil inzwischen ja neue Arbeitspläne angefallen sind.

Die Testfirma war zwar sechs Wochen alt, aber dort ließen sich die Zeiten für die benötigten Arbeitsgangzeilen rausladen und neu ins Echtsystem einspielen. Es blieben zwar dreißig Sätze übrig, die man noch händisch ankucken musste, aber dank Testfirma hat sich das Malheur nicht zur Katastrophe entwickelt.