Codeunit Parameter aus Dataport übergeben

4. Juli 2011 23:01

Hallo zusammen,

vorab: ich bin in navision ziemlich fit (was die nutzerseite angeht) - leider aber überhaupt nicht in C/AL.

ich habe mit .net (unter zuhilfenahme der reportinglösung jetreports und der sdk eines etikettenprogramms) eine anwendung entwickelt, die u.a. Warenverteilungsscheine (Wareneingang) und Picklisten inkl. der dazugehörigen Etiketten erstellen kann (anhand derer können Wareneingänge unter berücksichtigung der Terminierung den korrekten Fertigungsaufträgen zugewiesen werden).

Darüber hinaus sollen nun diese Vorgänge mehr oder weniger automtisch verbucht werden.

Momentane Lösung:

Meine Applikation stellt der erforderlichen Daten zusammen und erlaubt dem Nutzer ggfs. nötige Korrekturen. Diese werden dann in einem hinterlegten Dataport entsprechender Form aufbereitet und in eine CSV gegeben. Über direkten Aufruf eines Fertigungsauftrages (Link) und anschließendem senkeys wird dieser eingelesen (Ein Button mit Shortcut wurde dafür angelegt.) Das funktioniert eigentlich an dieser Stelle sehr zuverlässig. (Schöner wäre es nur den Dataport direkt per Link aufzurufen... glaube das geht nicht?)

Anschließend ruft mein Programm jeden zu buchenden Fertigungsauftrag genau einmal auf und sendet den Befehl per Sendkeys. Dazwischen liegen noch ein paar "Failsafe"-funktionen wie das abfangen des Titel des sich im Vordergrund befindlichen Fensters etc.

Trotzdem liegt die Erfolgsrate hier nur bei 90 - 95 %. Die Sendkey-Geschichten sind mir fürs Buchen eigentlich nicht so geheuer.

Um meinen Roman an dieser Stelle zu beenden: Ich möchte nachdem der Dataport eingelesen wurde direkt die Codeunit für das Buchen aufrufen. Das würde ich ja noch so ungefähr hinbekommen, aber wie kann ich jetzt der CodeUnit den erforderlichen Parameter (Ich nehme an REC Fertigungskopf --> NR.), am besten natürlich direkt aus dem Dataport übergeben?

Ich würde dann sinnvollerweise den Dataport um ein weiteres Dataitem (Fertigungskopf) erweitern, damit jeder FA nur einmal gebucht werden muss.

Ist es überhaupt möglich ein bestimmtes (oder mehrere) Items aus dem Dataport in eine Variable zu überführen und der Codeunit zu übergeben? Vielleicht kann mich ja einer in die richtige Richtung stoßen....

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 09:19

Grundsätzlich möchte ich vorab erwähnen, daß eine Lösung mit SendKeys immer mit Vorsicht zu geniessen ist. Ich sehe das immer wieder, daß einige Programmierer das als Lösung ansehen, wobei das aus meiner Sicht immer nur ein Workaround sein kann. Da du es verwendest, wirst du bereits wissen, daß das nur eine Emulation einer Benutzereingabe ist und kein echter programmtechnischer Code. Das kann immer zu Problemen führen, wie z.B. mit Latenzzeiten, Multilanguagefähigkeit,...

Zum Thema Codeunit aus Dataport aufrufen:
Die Transaktion muß beendet werden, wenn du Codeunit.RUN verwendest. Und ja, man kann mehrere DataItems im Dataport verwenden, allerdings keine eingerückten. Diese werden dann schlicht und ergreifend einfach nacheinander abgearbeitet.

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 09:49

HattrickHorst hat geschrieben:Grundsätzlich möchte ich vorab erwähnen, daß eine Lösung mit SendKeys immer mit Vorsicht zu geniessen ist. Ich sehe das immer wieder, daß einige Programmierer das als Lösung ansehen, wobei das aus meiner Sicht immer nur ein Workaround sein kann. Da du es verwendest, wirst du bereits wissen, daß das nur eine Emulation einer Benutzereingabe ist und kein echter programmtechnischer Code. Das kann immer zu Problemen führen, wie z.B. mit Latenzzeiten, Multilanguagefähigkeit,...

Zum Thema Codeunit aus Dataport aufrufen:
Die Transaktion muß beendet werden, wenn du Codeunit.RUN verwendest. Und ja, man kann mehrere DataItems im Dataport verwenden, allerdings keine eingerückten. Diese werden dann schlicht und ergreifend einfach nacheinander abgearbeitet.


Ich sehe das Problem, daher möchte ich es ja gerne anders lösen.

Meine Frage zielt an dieser Stelle eher in Richtung der zu verwendenden Syntax und wie die entsprechenden Variablen hinterlegt werden müssen - an dieser Stelle scheitert es leider bei mir. Wie gesagt - ich kenne mich mit C/AL überhaupt nicht aus und habe mich da nur mehr oder weniger reingefuchst.

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 12:57

Nur nochmal, damit ich das richtig verstehe. Du liest eine ganze Reihe von Fertigungsaufträgen per Dataport ein und gehst dann jeden einzelnen, neuen Auftrag auf der Karte durch und löst dort per SendKeys jeweils immer mit F11 den Buchungsvorgang inkl. aller Bestätigungen aus?

Einer Codeunit einen Datensatz mitzugeben, ist nicht besonders schwierig. Guck es dir einfach mal im Standard an, z.B. beim Erstellen eines Auftrags aus einem Angebot. Es gibt in der Codeunit ein Property TableNo, mit welchem du steuern kannst, welchen Datensatz (also von welcher Tabelle) die Codeunit beim Aufruf mit RUN aufnehmen soll.

Eine andere relativ einfache Variante wäre eine Funktion in der Codeunit zu definieren, der du immer den Datensatz als Parameter mitgibst, d.h. SetProductionOrder(Rec "Production Order") oder ähnliches. Danach muß die Codeunit natürlich mit genau diesem Datensatz weiterarbeiten und sie darf nicht zwischendurch initialisiert werden.

Es gibt noch andere Möglichkeiten, aber die braucht man eigentlich nur in besonderen Situationen und die liegen hier, soweit ich das im Moment sehen kann, nicht vor.

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 13:20

HattrickHorst hat geschrieben:Nur nochmal, damit ich das richtig verstehe. Du liest eine ganze Reihe von Fertigungsaufträgen per Dataport ein und gehst dann jeden einzelnen, neuen Auftrag auf der Karte durch und löst dort per SendKeys jeweils immer mit F11 den Buchungsvorgang inkl. aller Bestätigungen aus?

Einer Codeunit einen Datensatz mitzugeben, ist nicht besonders schwierig. Guck es dir einfach mal im Standard an, z.B. beim Erstellen eines Auftrags aus einem Angebot. Es gibt in der Codeunit ein Property TableNo, mit welchem du steuern kannst, welchen Datensatz (also von welcher Tabelle) die Codeunit beim Aufruf mit RUN aufnehmen soll.

Eine andere relativ einfache Variante wäre eine Funktion in der Codeunit zu definieren, der du immer den Datensatz als Parameter mitgibst, d.h. SetProductionOrder(Rec "Production Order") oder ähnliches. Danach muß die Codeunit natürlich mit genau diesem Datensatz weiterarbeiten und sie darf nicht zwischendurch initialisiert werden.

Es gibt noch andere Möglichkeiten, aber die braucht man eigentlich nur in besonderen Situationen und die liegen hier, soweit ich das im Moment sehen kann, nicht vor.


Derzeit ermittelt mein Programm die erforderlichen Buchungen und schreibt die CSV und liest diese per Dataport ein. Dabei werden bisher NUR FertigungsZEILEN importiert.

Anschließend ermittelt meine Anwendung die zu buchunden FAs und führt selbstständig den Aufruf von FAs und dem anschließenden Buchen per keysend aus.

Ich kann leider KEINE codunit bearbeiten (habe auch nicht die Fähigkeiten dazu). Die Codeunit "Buchen" hat bereits den (einzigen) Parameter FA-kopf REC. (FA-Nr.).

Eigentlich möchte ich gerne nach import der Daten im Dataport hinterlegen, das die Codeunit Buchen ausgeführt werden soll. Dazu muss ich halt einen Parameter übergeben (Codeunit.run (50000125, FA0110256) oder so ähnlich. Der Parameter ist bereits so in der codeunit implementert.

Ich weiß an dieser Stelle nur nich wie ich es hinbekomme (nur im dataport, nicht in einer codeunit) wie ich diesen parameter zum einen korrekt als record deklariere und zum anderen werte aus dem dataport (Belegnr. aus der Fertigungszeile z.B. oder ein weiteres dataitem FA-Kopf im dataport das nur als buchungsvorlage dient ohne daten zu überschreiben) auslese und für jeden die buchungscodeunit ausführe.

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 13:37

Wenn du das alles nicht weißt, wie hast du dann deine bisherige Lösung hinbekommen? Erstaunlich!

Also, da brauche ich doch noch etwas mehr Informationen:
Sind das alles nur Zeilen eines FAs, die da pro Dataportaufruf eingelesen werden? Wenn ja, mußt du ja an irgendeiner Stelle den passenden Kopf dazu erstellen lassen (oder siehe unten). Diesen Record kannst dann verwenden, um ihn der Codeunit zu übergeben (CODEUNIT.RUN(CODEUNIT::Nummer,DeinFAkopf)). Das ist aber etwas gefährlich, da die Transaktion bei einem Dataport erst am Ende beendet sein/werden sollte, also das implizite Transaktionsende. Baut man jetzt noch ein explizites irgendwo dazwischen ein, weil man bspw. Codeunit.RUN benutzen möchte, kann es passieren, je nach Konstellation, daß nur ein Teil der Daten eingelesen wurde. Also, von dieser Version würde ich dringend abraten. Es sei denn....

Sehe ich das richtig, daß du die Daten nach dem Import gar nicht speichern möchtest, sondern die CSV-Datei nur als Liste aller zu buchenden FA-Zeilen verwendest?

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 14:17

HattrickHorst hat geschrieben:Wenn du das alles nicht weißt, wie hast du dann deine bisherige Lösung hinbekommen? Erstaunlich!


meine bisherige Lösung ist eine .net applikation. Das krieg ich schon hin (-; Ich bin aber von Haus aus kein Programmierer und ich habe wie erwähnt keinerlei Kenntnisse in C/AL bzw. der Art und Weise wie Navision Variablen verwendet.

HattrickHorst hat geschrieben:Sind das alles nur Zeilen eines FAs, die da pro Dataportaufruf eingelesen werden? Wenn ja, mußt du ja an irgendeiner Stelle den passenden Kopf dazu erstellen lassen (oder siehe unten).


Nein, es sind mehrere FAs. Diese existieren aber alle schon. Es geht im wesentlichen darum in diesen FAs das Feld "Menge / Zeit Buchung" zu füllen (und ggfs. Korrekturen am LAgerort etc. vorzunehmen.

HattrickHorst hat geschrieben:Diesen Record kannst dann verwenden, um ihn der Codeunit zu übergeben (CODEUNIT.RUN(CODEUNIT::Nummer,DeinFAkopf)). Das ist aber etwas gefährlich, da die Transaktion bei einem Dataport erst am Ende beendet sein/werden sollte, also das implizite Transaktionsende. Baut man jetzt noch ein explizites irgendwo dazwischen ein, weil man bspw. Codeunit.RUN benutzen möchte, kann es passieren, je nach Konstellation, daß nur ein Teil der Daten eingelesen wurde. Also, von dieser Version würde ich dringend abraten. Es sei denn....

Sehe ich das richtig, daß du die Daten nach dem Import gar nicht speichern möchtest, sondern die CSV-Datei nur als Liste aller zu buchenden FA-Zeilen verwendest?
[/quote]

Nein, wie oben geschrieben wird der Import der Fertigungszeilen benötigt und auch gespeichert (das ist die Buchungsgrundlage).

Das Funktioniert ja auch schon. ich will aber im nachinein nur ungerne für 10, 20 oder 30 FAs meine .net applikation die sendkeysoperation (rufe FA auf --> drücke shortcut für buchen --> warte auf fenstertitel --> schließe FA etc...) durchführen lassen. Das ist einfach nicht zuverlässig genug (und auch noch nicht im Produktiveinsatz)

Meine Überlegung war an dieser Stelle jetzt dem Dataport ein weiteres DataItem zu ergänzen (FA-Kopf). Diese Daten würden dann nur die Informationen fürs Buchen erhalten (also eigentlich nur die FA-Nr.)

Auf diese Weise könnte ich alle Buchungen importieren (in verschiedene FAs) und speichern,
so wie anschließend die Liste der zu buchenden FAs importieren (damit jeder nur einmal gebucht wird).

beim 2. DataItem würde ich jetzt gerne bei: Fertigungskopf - OnPostDataItem()

so etwas wie CODEUNIT.RUN(5013660, FA; benutzen.

Genau hier ist mein Problem. Wie komme ich jetzt von den FAnummern aus dem DataItem zur Variable "FA" (die habe ich jetzt mal zum testen unter C/AL Globals, Datatype Record und Subtype Fertigungskopf eingetragen)?


Wenn ich das richtig verstehe kann ich es ja sogar besser in Fertigungskopf - OnAfterImportRecord()

____________________________________________

Während ich das alles geschrieben habe hab ich es hinbekommen.

in Fertigungskopf - OnAfterImportRecord() habe ich jetzt einfach:

CODEUNIT.RUN(5013660, Fertigungskopf); geschrieben. So funktioniert es. On PostDataItem ist er ja theoretisch schon mit allem fertig und kennt gar keinen Fertigungskopf mehr.

Jetzt stellt sich eigentlich nur noch die Frage was er bei mehreren FAs macht. Ich glaube das hast du oben schon angerissen. Hier meine Vorstellung:

Erstes Dataitem Fertigungszeilen (Keine aufgerufene Codeunit, nur Dataimport) wird erst mal komplett abgeschlossen.

Zweites Dataitem Fertigungskopf (enthält nur eine Liste mit den zu buchenden FAs und bei Fertigungskopf - OnAfterImportRecord() (oder OnBefore) den Code siehe oben.
Wartet der Dataport an dieser Stelle bis die CodeUnit (buchen) durchgelaufen ist und importiert dann das nächste record?

Wenn er an dieser Stelle fleißig weiterimportiert (oder auch nicht) unbabhängig davon ob die codeunit nun abgeschlossen ist oder nicht bekomme ich hier natürlich schwieriegkeiten.

Darüber hinaus bin ich aber allein schon durch unsere Unterhaltung ein GANZES Stück weiter! Vielen Dank schon mal!

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 15:09

Also, wenn du nur FA-Zeilen in deiner CSV-Datei hast, dann brauchst du kein DataItem für den FA-Kopf. Schließlich hast du ja gar nichts, was du hier importieren kannst. Außerdem würden erst alle FA-Zeilen durchlaufen werden und anschließend erst die FA-Köpfe. Das geht so nicht.

Es reicht aber völlig aus, eine globale Variable vom Typ Record auf den FA-Kopf anzulegen, die dann jeweils auf den richtigen Datensatz verweisen soll. Nur, wenn die erste Zeile eines FAs eingelesen wird, woher weißt du dann, wieviele Zeilen für diesen FA noch kommen? Du willst ja sicher erst alle Zeilen im FA haben, bevor du ihn buchst.

Theoretisch würde ein Codeunit-Aufruf sequentiell in den Code eingeschoben werden, er arbeitet also im Hintergrund nicht weiter. Allerdings kann man Codeunit.RUN nicht in einer laufenden Schreibtransaktion, was ein Datenimport per Dataport ist, verwenden. Dazu müßte man vorab ein explizites Transaktionsende setzen, was aber nur in ganz besonderen Ausnahmefällen empfehlenswert ist. Prinzipiell würde ich deine Lösung eher anders aufbauen. Wenn du keine Codeunit ändern kannst, solltest du das vielleicht mal mit eurem Partner besprechen.

Wann welche Trigger durchlaufen werden, steht in der Regel ganz gut in der Online-Hilfe beschrieben. http://msdn.microsoft.com/en-us/library/dd338918.aspx

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 15:55

HattrickHorst hat geschrieben:Also, wenn du nur FA-Zeilen in deiner CSV-Datei hast, dann brauchst du kein DataItem für den FA-Kopf. Schließlich hast du ja gar nichts, was du hier importieren kannst. Außerdem würden erst alle FA-Zeilen durchlaufen werden und anschließend erst die FA-Köpfe. Das geht so nicht.


Deswegen ja der Trick mit 2 Dataitems.

DataItem1: alle fertigungszeilen und damit alle buchungsrelevanten Daten sind in Navision. Hier kann nichts schiefgehen, oder?

Schritt 1 abgeschlossen (Buchungsdaten in Navision)

DataItem2: Fertigungskopf (hier stehen nur FA-nummern und die anderen primary keys drin) hier sollen KEINE daten importiert werden. Das dient nur zum identifizieren der FAs für die die CodeUnit ausgeführt wird.
für jede Zeile von Dataitem 2 wird für den entsprechenden FA die Codeunit ausgeführt und dann der nächste angepackt

Möglichkeit 1: Es treten beim Buchen (Ausführen der Codeunits) keine Probleme auf. Es wurde also alles gebucht. (Schritt 2 abgeschlossen)

Möglichkeit 2: Es treten Probleme beim Buchen (also ausführen der Codeunits) Probleme auf. Es wird NICHT alles gebucht und der Import bricht ab, aber alle Buchungsrelevanten Daten aus Schritt 1 sind ja schon vorher importiert worden. Schritt 2 enthält keine wichtigen zu importierenden Daten.
Man müsste hier also nur eine Prüfroutine einbauen um die Bedingungen für ein Erfolgreiches Buchen herzustellen (einstandspreis für Artikel etc.) und das Buchen für diese FAs wiederholen

Kleines (vereinfachtes Beispiel:)

Code:
Menge_Buchung - FA - Zeilennr.   <--Erstes DataItem, Fertigungszeile mit einigen Records die auch importiert werden soll
1;FA0110650;10000
5;FA0110650;20000
3;FA0110650;30000
10;FA0110655;40000
1;FA0110656;10000     
                                                   <-- Import erstes DataItem Fertigungszeile abgeschlossen, alles ist auf jeden Fall in Navision

FA-Nr.                                           <-- Zweites DataItem, Fertigungskopf, keine Daten die verändert werden, nur für die Codeunit
FA0110650
(CODEUNIT OnAfterImportRecord BUCHEN FA0110650) <-- Nur zur veranschaulichung, steht natürlich nicht im dataport / in der csv
FA0110655
(CODEUNIT OnAfterImportRecord BUCHEN FA0110650) <-- Nur zur veranschaulichung, steht natürlich nicht im dataport / in der csv
FA0110656
(CODEUNIT OnAfterImportRecord BUCHEN FA0110650) <-- Nur zur veranschaulichung, steht natürlich nicht im dataport / in der csv



^^ So ungefähr stelle ich mir das vor

Re: Codeunit Parameter aus Dataport übergeben

5. Juli 2011 16:14

Die Transaktion wird durch das Starten und Beenden des Dataports bestimmt und nicht durch einzelne DataItems. Wenn du ein explizites Transaktionsende nach dem ersten oder vor dem zweiten DataItem setzen möchtest, hast du die Datei noch im Zugriff. Außerdem müßte nach jedem Buchungsvorgang ein weiteres Transaktionsende gesetzt werden, weil die vorausgehende Buchung ja noch gar nicht in der Datenbank angekommen wäre. Ich finde, das ist ein sehr wackeliges und riskantes Konstrukt, das man eigentlich viel einfacher und sicherer haben könnte.