PowerShell: C/AL in AL für Extension überführen

14. Dezember 2017 17:31

Um C/AL in AL zu überführen sind mehrere fummelige Schritte notwendig. Waldo hatte hier schon dazu berichtet. Mittlerweile kursieren auch viele weitere Skripte u.a. mit Dockercontainern etc.

Nachtrag 2018: Beim Einsatz von Docker geht dieses mit nur einem Container jetzt wesentlich einfacher als direkter Export mittels des Cmdlets Convert-ModifiedObjectsToAL.

In dem alten Artikel vom Mai 2017 noch nicht erwähnt: Um eine Grundlage für eine Extension zu haben, müssen nach dem Zwischenexport DELTA-Dateien gebildet werden, erst dann entstehen beim txt2al-Lauf Modification-Dateien in AL für Table- oder Pageextensions im Bezug auf die Standardobjekte (aus der Base App). Die Fähigkeit, DELTA-Dateien überhaupt zu verarbeiten, bekam txt2al.exe ja auch erst einen Monat später :wink: .

Extensions1.png


Der Anfang der ersten Datei oben für eine neue Action in der Bankkontokarte sieht dann bspw. so aus
Code:
pageextension 70000002 pageextension70000002 extends "Bank Account Card"
{
    // version NAVW111.00,MyExt01

    actions
    {
        addafter("Bank&konto")


Hier neue Schlüssel in Tabelle 21 Cust. Ledger Entry (falls übrigens wie hier ausschließlich neue Schlüssel das DELTA bilden, kann man das Objekt gleich wieder löschen, denn solche werden vom SQL-Server nicht mehr benötigt und der AL-Compiler stuft diese als Fehler ein).
Docs: Restrictions on key modifications ([Nachtrag 16.12.21]: Geht ab 18.x wieder, siehe hier, daher hier jetzt durchgestrichen, der damalige Abschnitt im Learn-Artikel wurde auch entfernt, Limitations and Restrictions sind weiterhin gültig).
Code:
tableextension 70000009 tableextension70000009 extends "Cust. Ledger Entry"
{
    // version NAVW111.00,MyExt01

    keys
    {
        key(Key1;"Customer No.","Document No.")
        {
        }
        key(Key2;"Customer No.",Open,"Document Date")
        {
        }
        key(Key3;Open,"Customer No.","Document Date")
        {
        }
    }
}



Ablauf
  • Objekte aus der MODIFIED-DB (mit der Extension) über Versionslistenfilter in der temporären Zwischensyntax exportieren ( Schalter -ExportToNewSyntax)
  • Die modifizierten Base App-Objekte der MODIFIED-Datenbank aus der ORIGINAL-Datenbank über eine temporäre Indexliste ebenfalls in der Zwischensyntax exportieren
  • DELTA-Dateien für diese Objekte bilden. DELTA-Dateien für Objekte außerhalb der Base App sind identisch mit einem normalem Objektexport in Zwischensyntax, enthalten also jeweils den kompletten Code des Objekts da es ja kein Ausgangsobjekt in der ORIGINAL-Datenbank dazu gibt.
  • DELTA-Dateien mit txt2al.exe in AL umwandeln

Dieses Skript erledigt das automatisch. Ich gehe dabei beim Export direkt auf die Datenbanken und nicht wie Waldo über die Dienste an diese heran, das geht weiterhin auch bzw. hier noch. In VS Code bzw. AL geht es dann aber nur noch mit dem laufenden Dienst, da man sich hier nur noch so mit der Datenbank verbinden kann.
Im Skript müssen MyServer, MyDatabase,Versionslistenfilter usw. natürlich an die eigenen Umgebungen angepasst werden. Bei uns habe ich das so umgesetzt, dass diese Skripte automatisch mit den richtigen Parametern generiert werden und die AL-Dateien somit per Mausklick erzeugt werden können.

Der Arbeitsordner sieht dann bspw. so aus, in AL liegen die fertigen .al- und .rdlc-Dateien.
CAL2AL.png

Die BaseAppObjects.txt sind die Standardobjekte für Kontrollzwecke gebenüber dem Extensionobjektpaket in normaler C/AL-Syntax.
Links C/AL, rechts die Zwischensyntax, die über den Schalter -ExportAsNewSyntax erzeugt wird.
CompareOldNewSyntax.png

In ORIGINAL die gleichen Objekte der Base App, aber einzeln zerlegt und dort dann ebenfalls in Zwischensyntax. Hierauf setzt das Compare-Cmdlet an, um die Grundlage für txt2al.exe zu liefern.

Code:
[string]$MyEnviron = [Environment]::OSversion.Version.ToString(3) ; [bool]$OldEnviron = ($MyEnviron.substring(0,3) -eq '6.1')
$ParentWorkingFolder = "C:\MyFolder"
IF (!(Test-Path $ParentWorkingFolder)) {New-Item $ParentWorkingFolder -type directory}
IF (!(Test-Path $ParentWorkingFolder\ORIGINAL)) {New-Item $ParentWorkingFolder\ORIGINAL -type directory -force}
IF (!(Test-Path $ParentWorkingFolder\MODIFIED)) {New-Item $ParentWorkingFolder\MODIFIED -type directory -force}
IF (!(Test-Path $ParentWorkingFolder\DELTA)) {New-Item $ParentWorkingFolder\DELTA -type directory -force}
Import-Module "${env:ProgramFiles(x86)}\Microsoft Dynamics NAV\110\RoleTailored Client\Microsoft.Dynamics.Nav.Model.Tools.psd1" -force -DisableNameChecking | out-null
$IndexFolder = "$ParentWorkingFolder\TEMPINDEX"
IF (Test-Path $IndexFolder) {Remove-Item $IndexFolder\*.txt -recurse}
Export-NAVApplicationObject -DatabaseServer MyServer -DatabaseName MyDatabase -ExportToNewSyntax -path $ParentWorkingFolder\OPPobjectsNewSyntax.txt -Filter "Version List=*OPP*" -ExportTxtSkipUnlicensed -Force
Split-NAVApplicationObjectFile -Source $ParentWorkingFolder\OPPObjectsNewSyntax.txt -Destination $ParentWorkingFolder\MODIFIED -Force
Export-NAVApplicationObject -DatabaseServer MyServer -DatabaseName MyDatabase -path $ParentWorkingFolder\BaseAppObjects.txt -Filter 'ID=..49999;"Version List=*OPP*"' -ExportTxtSkipUnlicensed -Force
Split-NAVApplicationObjectFile -Source $ParentWorkingFolder\BaseAppObjects.txt -Destination $ParentWorkingFolder\TEMPINDEX -Force
Get-ChildItem -Path $IndexFolder\*.txt |
ForEach-Object `
(
{
Write-host "Trying to export object:" $_.Name
$IDbasename = [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
$IDobject = $IDbasename.Substring(3)
$ShortObjectType = $IDbasename.Substring(0,3)
$IDobject = "ID=" + $IDobject
Switch ($ShortObjectType)
{"TAB" {$ObjectFilter = "Type=Table;" + $IDobject + '"'}
"PAG" {$ObjectFilter = "Type=Page;" + $IDobject + '"'}
"REP" {$ObjectFilter = "Type=Report;" + $IDobject + '"'}
"XML" {$ObjectFilter = "Type=XMLport;" + $IDobject + '"'}
"COD" {$ObjectFilter = "Type=Codeunit;" + $IDobject + '"'}
"MEN" {$ObjectFilter = "Type=MenuSuite;" + $IDobject + '"'}
"QUE" {$ObjectFilter = "Type=Query;" + $IDobject + '"'}}
$ExportFile = $_.Name
$LogFile = "$ParentWorkingFolder\ORIGINAL\Log_$ExportFile"
$ExportFile = "$ParentWorkingFolder\ORIGINAL\$ExportFile"
if (Test-Path "$ParentWorkingFolder\navcommandresult.txt") {Remove-Item "$ParentWorkingFolder\navcommandresult.txt"}
if (test-path $ExportFile) {remove-item $ExportFile}
if ($OldEnviron) {$NAVFolder = '"C:\Program Files (x86)\Microsoft Dynamics NAV\110\RoleTailored Client'} else {$NAVFolder = 'C:\Program Files (x86)\Microsoft Dynamics NAV\110\RoleTailored Client'}
$exportfinsqlcommand = """$NAVFolder\finsql.exe"" command=ExportToNewSyntax,file=$ExportFile,servername=MyServer,database=MyDatabase,Logfile=$LogFile"
if ($ObjectFilter -ne ""){$exportfinsqlcommand = "$exportfinsqlcommand,filter=$ObjectFilter"}
$Command = $exportfinsqlcommand
Write-Debug $Command
cmd /c $Command
$ExportFileExists = Test-Path "$ExportFile"
If (-not $ExportFileExists) {
write-error "Error on exporting to $ExportFile. Look at the information below."
if (Test-Path "$ParentWorkingFolder\navcommandresult.txt"){Get-Content "$ParentWorkingFolder\navcommandresult.txt"}
if (Test-Path $LogFile) {Get-Content $LogFile}
}
else{
$NAVObjectFile = Get-ChildItem $ExportFile
if ($NAVObjectFile.Length -eq 0) { Remove-Item $NAVObjectFile }
if (Test-Path "$ParentWorkingFolder\navcommandresult.txt") {Get-Content "$ParentWorkingFolder\navcommandresult.txt"}
}}
)
if (Test-Path "$ParentWorkingFolder\ORIGINAL\navcommandresult.txt") {Remove-Item "$ParentWorkingFolder\ORIGINAL\navcommandresult.txt"}
IF (Test-Path $IndexFolder) {Remove-Item -path $IndexFolder\*.txt -recurse}
IF (Test-Path $IndexFolder) {Remove-Item -path $IndexFolder}
$NAVFolder = "${env:ProgramFiles(x86)}\Microsoft Dynamics NAV\110\RoleTailored Client"
Compare-NAVApplicationObject -DeltaPath $ParentWorkingFolder\DELTA -ModifiedPath $ParentWorkingFolder\MODIFIED -OriginalPath $ParentWorkingFolder\ORIGINAL -Force -IgnoreDocumentation
$Convertcommand = """$NAVFolder\txt2al.exe"" --source=""$ParentWorkingFolder\DELTA"" --target=""$ParentWorkingFolder\AL"" --rename"
$Command = $Convertcommand
Write-Host -ForegroundColor Green 'Convert objects with:'
Write-host -ForegroundColor Gray "  $Command"
cmd /c $Command
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: C/AL in AL für Extension überführen

24. April 2018 17:43

txt2al.exe hat neuerdings ein verändertes Verhalten, die aktuellen (z.B. aus den Docker-Sandbox-Containern) setzen zwischen Variablenname und Doppelpunkt kein Leerzeichen und auch kein Semikolon am Ende der Procedure-Zeile mehr, was beim Abgleich mit Objekten aus älteren Konvertierungen massenweise Diffs produziert :roll: .
txt2al_altneu.png

txt2al_altneu2.png
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: C/AL in AL für Extension überführen

2. Mai 2018 16:14

Wer mit einem Dockercontainer arbeitet, kann für diese Konvertierung auch das Cmdlet Convert-ModifiedObjectsToAL aus dem Modul navcontainerhelper nutzen.
ConvertModToAL.png

Hier ist keine getrennte ORIGINAL-Datenbank notwendig, da das Delta anhand der Modifikationen gegen den Auslieferungszustand des Containers gebildet wird (die sogenannte Baseline der Objekte). Dazu müssen lediglich die Objekte bei der Erstellung in den Ordner ORIGINAL exportiert werden, bevor Änderungen daran vorgenommen werden, also bei New-NAVContainer nicht den Parameter -doNotExportObjectsToText einschalten oder besser hierfür New-CSideDevContainer verwenden ) (Nachtrag: Link mittlerweile verwaist wegen Abschaltung von C/SIDE in BC 15)

Die Pfade hierfür sind C:\ProgramData\NavContainerHelper\Extensions\<Containername>\original-newsyntax für die Baseline bzw. nach dem Erstellen und Modifizieren der Objekte im Container für das Konvertierungsergebnis …\<Containername>\al-newsyntax, mit dessen Inhalt man dann ein neues AL-Projekt in VS Code starten kann.

Da bei Txt2Al.exe seit der Bereitstellung viele Bugs gefunden und behoben wurden, hierfür ein aktuelles Image verwenden.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: C/AL in AL für Extension überführen

9. August 2018 12:07

Kowa hat geschrieben:Da bei Txt2Al.exe seit der Bereitstellung viele Bugs gefunden und behoben wurden, hierfür ein aktuelles Image verwenden.

Leider nur zu wahr :mrgreen:.
Da ich bei Konvertierung eines Systems mit älterer Technik (ein halbes Jahr alt…) den Effekt hatte, dass einige Reports nach der Konvertierung mit Convert-ModifiedObjectsToAL fehlten (ohne dass dabei eine Fehlermeldung erschien :roll:), habe ich die fehlenden noch einmal mit aktueller Previewtechnik (also Version 13) konvertiert.
Mit Convert-Txt2Al wird nur die technische Plattform des Containers genutzt (also nicht die Objekte des Containers wie bei Convert-ModifiedObjectsToAL), was in diesem Fall auch erforderlich war, um überhaupt alle erwarteten Dateien (.al, .rdlc und .xlf) zu erhalten.
ConvertTxt2AL.png


Blogartikel zum Txt2AL Tool.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: C/AL in AL für Extension überführen

16. Dezember 2021 19:19

Kowa hat geschrieben:Hier neue Schlüssel in Tabelle 21 Cust. Ledger Entry (falls übrigens wie hier ausschließlich neue Schlüssel das DELTA bilden, kann man das Objekt gleich wieder löschen, denn solche werden vom SQL-Server nicht mehr benötigt und der AL-Compiler stuft diese als Fehler ein).

Ab BC 18.x (AL Runtime 7.0) wurde das wieder ermöglicht und der Compiler akzeptiert ab da solche Objekte.
Partners can add keys (indexes) to base tables and table extensions
Limitations and Restrictions
Yun Zhu: New Features for Dynamics 365 Business Central 2021 release wave 1 (BC18): Creating new keys in a tableextension with fields from the base table