Initial commit – AufmaßCreater v2.35

This commit is contained in:
2026-06-10 11:03:43 +02:00
commit 84c933ea9c
2823 changed files with 490495 additions and 0 deletions
+680
View File
@@ -0,0 +1,680 @@
#include <Array.au3>
#include <File.au3>
#include <Date.au3>
#include <Debug.au3>
#include <GUIConstantsEx.au3>
; =========================================================
; INDUSTRIAL AUTOIT REB 23.003 → GAEB X31 ENGINE
; kompatibel mit DATAflor / California / GAEB DA XML
; =========================================================
Global $INPUT_FILE = @ScriptDir & "\..\Liebenau_BA_5_Mengennachweiß_AZ002_.txt"
Global $OUTPUT_FILE = @ScriptDir & "\..\export2.x31"
Global $g_iIDCnt = 1000000
; ── Einstiegspunkt ──────────────────────────────────────────
Local $importPfadTxt = @ScriptDir & "\..\Meckenbeueren_AZ10_.txt"
Local $ausgabePfadX31 = @ScriptDir & "\..\Meckenbeueren_AZ10_x31neu.x31"
Local $templateX31 = @ScriptDir & "\..\exporthallo.x31"
_X31_Export($importPfadTxt, $ausgabePfadX31)
Func _X31_Export($sImportTxt, $sAusgabeX31)
; ── 1. Datei einlesen ────────────────────────────────────
Local $hLese = FileOpen($sImportTxt, 0)
If $hLese = -1 Then
MsgBox(16, "Fehler", "Importdatei nicht gefunden:" & @CRLF & $sImportTxt)
Return SetError(1, 0, False)
EndIf
Local $aRawLines[10001]
$aRawLines[0] = 0
While True
Local $sZ = FileReadLine($hLese)
If @error = -1 Then ExitLoop
$sZ = StringReplace($sZ, Chr(13), "")
If $aRawLines[0] < 10000 Then
$aRawLines[0] += 1
$aRawLines[$aRawLines[0]] = $sZ
EndIf
WEnd
FileClose($hLese)
If $aRawLines[0] = 0 Then
MsgBox(16, "Fehler", "Importdatei ist leer.")
Return SetError(2, 0, False)
EndIf
; ── 2. Kopfdaten lesen ───────────────────────────────────
Local $aKopf[30][2]
Local $iKopfN = 0
Local $bInKopf = False
For $i = 1 To $aRawLines[0]
Local $sL = StringStripWS($aRawLines[$i], 3)
If StringInStr($sL, "[Kopfdaten]") Then
$bInKopf = True
ContinueLoop
EndIf
If StringLeft($sL, 1) = "[" And $bInKopf Then
$bInKopf = False
ContinueLoop
EndIf
If $bInKopf Then
Local $iG = StringInStr($sL, "=")
If $iG > 0 And $iKopfN < 28 Then
$aKopf[$iKopfN][0] = StringStripWS(StringLeft($sL, $iG - 1), 3)
$aKopf[$iKopfN][1] = StringStripWS(StringMid($sL, $iG + 1), 3)
$iKopfN += 1
EndIf
EndIf
Next
; Fallback: erste 15 Zeilen
If $iKopfN = 0 Then
For $i = 1 To ($aRawLines[0] < 15 ? $aRawLines[0] : 15)
Local $sF = StringStripWS($aRawLines[$i], 3)
Local $pF = StringInStr($sF, "=")
If $pF > 0 And $iKopfN < 28 Then
$aKopf[$iKopfN][0] = StringStripWS(StringLeft($sF, $pF - 1), 3)
$aKopf[$iKopfN][1] = StringStripWS(StringMid($sF, $pF + 1), 3)
$iKopfN += 1
EndIf
Next
EndIf
Local $sDatum = _KopfGet($aKopf, "Datum")
Local $sBaustelle = _KopfGet($aKopf, "Baustelle")
Local $sBauabs = _KopfGet($aKopf, "Bauabschnitt")
Local $sVertrag = _KopfGet($aKopf, "Vertrag")
Local $sAspaN = _KopfGet($aKopf, "AspaN")
Local $sAspaTel = _KopfGet($aKopf, "AspaTel")
Local $sAbrufNr = _KopfGet($aKopf, "AbrufNr")
Local $sSMNr = _KopfGet($aKopf, "SMNr")
Local $sStartZ = _KopfGet($aKopf, "StartZ")
Local $sEndZ = _KopfGet($aKopf, "EndZ")
Local $sISODatum = _DatumISO($sDatum)
Local $sStartISO = _DatumISO($sStartZ)
Local $sEndISO = _DatumISO($sEndZ)
Local $sUID = _UUID()
Local $sBoQUID = _UUID()
Local $sLVName = ($sVertrag <> "") ? $sVertrag : ($sBaustelle & " " & $sBauabs)
; ── 3. Aufmassdaten parsen ───────────────────────────────
; Spalten: 1=Ort|2=OZ|3=Faktor|4=Einzelmenge|5|6|7=GesamtMenge|
; 8=Einheit|9=Beschreibung|10=Bemerkung|11=Menge2|12=EP|13=GP
Local $aPosData[10001][9]
; [n][0]=OZ [n][1]=Qty(float) [n][2]=Ort [n][3]=Beschreibung
; [n][4]=Bemerkung [n][5]=Einheit [n][6]=EP [n][7]=Faktor [n][8]=EinzelMenge
Local $iPosN = 0
Local $bInData = False
For $i = 1 To $aRawLines[0]
Local $sLine = StringStripWS($aRawLines[$i], 3)
If StringInStr($sLine, "[Aufma") Then
$bInData = True
ContinueLoop
EndIf
If StringLeft($sLine, 1) = "[" And $bInData Then
$bInData = False
ContinueLoop
EndIf
If Not $bInData Then ContinueLoop
If $sLine = "" Then ContinueLoop
If StringStripWS(StringReplace($sLine, "|", ""), 3) = "" Then ContinueLoop
If Not StringInStr($sLine, "|") Then ContinueLoop
Local $aS = StringSplit($sLine, "|", $STR_NOCOUNT)
Local $nS = UBound($aS)
If $nS < 2 Then ContinueLoop
Local $sOZ = ($nS > 1) ? StringStripWS($aS[1], 3) : ""
If Not _IsValidOZ($sOZ) Then ContinueLoop
Local $sOrt = ($nS > 0) ? StringStripWS($aS[0], 3) : ""
Local $sFaktor = ($nS > 2) ? StringStripWS($aS[2], 3) : "1"
Local $sEinzel = ($nS > 3) ? StringStripWS($aS[3], 3) : ""
Local $sGesamt = ($nS > 6) ? StringStripWS($aS[6], 3) : ""
Local $sEinh = ($nS > 7) ? StringStripWS($aS[7], 3) : ""
Local $sBeschr = ($nS > 8) ? StringStripWS($aS[8], 3) : ""
Local $sBemerk = ($nS > 9) ? StringStripWS($aS[9], 3) : ""
Local $sEP = ($nS > 11) ? StringStripWS($aS[11], 3) : ""
; Qty berechnen: Faktor * Einzelmenge, oder direkt Gesamtmenge
Local $fFaktor = _ToFloat($sFaktor)
Local $fEinzel = _ToFloat($sEinzel)
Local $fGesamt = _ToFloat($sGesamt)
Local $fQty = 0
If $fFaktor <> 1 And $fEinzel <> 0 Then
$fQty = $fFaktor * $fEinzel
ElseIf $fGesamt <> 0 Then
$fQty = $fGesamt
ElseIf $fEinzel <> 0 Then
$fQty = $fEinzel
Else
$fQty = $fFaktor
EndIf
$aPosData[$iPosN][0] = $sOZ
$aPosData[$iPosN][1] = $fQty
$aPosData[$iPosN][2] = $sOrt
$aPosData[$iPosN][3] = $sBeschr
$aPosData[$iPosN][4] = $sBemerk
$aPosData[$iPosN][5] = $sEinh
$aPosData[$iPosN][6] = $sEP
$aPosData[$iPosN][7] = $sFaktor
$aPosData[$iPosN][8] = $sEinzel
$iPosN += 1
If $iPosN >= 10000 Then ExitLoop
Next
If $iPosN = 0 Then
MsgBox(16, "Fehler", "Keine gueltigen Positionen gefunden.")
Return SetError(3, 0, False)
EndIf
; ── 4. OZ-Schema ermitteln ───────────────────────────────
; Bestimme Typ und maximale Segmentlängen
Local $sOZTyp = "A"
Local $iMaxL1 = 2
Local $iMaxL2 = 2
Local $iMaxL3 = 2
Local $iMaxPos = 4
For $i = 0 To $iPosN - 1
Local $aOZ = _OZInfo($aPosData[$i][0])
If $aOZ[0] = "B" Then $sOZTyp = "B"
If $aOZ[0] = "C" And $sOZTyp = "A" Then $sOZTyp = "C"
If $aOZ[6] > $iMaxL1 Then $iMaxL1 = $aOZ[6]
If $aOZ[7] > $iMaxL2 Then $iMaxL2 = $aOZ[7]
If $aOZ[8] > $iMaxL3 Then $iMaxL3 = $aOZ[8]
If $aOZ[9] > $iMaxPos Then $iMaxPos = $aOZ[9]
Next
; ── 5. OZ-Hierarchie aufbauen ────────────────────────────
Local $aTitel[100]
Local $iTitelN = 0
Local $aUTitel[1000][3] ; [n][0]=E1 [n][1]=E2 [n][2]=E3(nur Typ B)
Local $iUTitelN = 0
For $i = 0 To $iPosN - 1
Local $aOZI = _OZInfo($aPosData[$i][0])
Local $sE1 = $aOZI[1]
Local $sE2 = $aOZI[2]
Local $sE3 = $aOZI[3]
; Titel (Ebene 1)
If $sE1 <> "" Then
Local $bGefT = False
For $j = 0 To $iTitelN - 1
If $aTitel[$j] = $sE1 Then
$bGefT = True
ExitLoop
EndIf
Next
If Not $bGefT Then
$aTitel[$iTitelN] = $sE1
$iTitelN += 1
EndIf
EndIf
; Untertitel (Ebene 2 + ggf. Ebene 3)
If $sE1 <> "" Then
Local $bGefU = False
For $j = 0 To $iUTitelN - 1
If $aUTitel[$j][0] = $sE1 And $aUTitel[$j][1] = $sE2 And $aUTitel[$j][2] = $sE3 Then
$bGefU = True
ExitLoop
EndIf
Next
If Not $bGefU Then
$aUTitel[$iUTitelN][0] = $sE1
$aUTitel[$iUTitelN][1] = $sE2
$aUTitel[$iUTitelN][2] = $sE3
$iUTitelN += 1
EndIf
EndIf
Next
; ── 6. Zeit ──────────────────────────────────────────────
Local $sTime = StringRight("0" & @HOUR, 2) & ":" & StringRight("0" & @MIN, 2) & ":" & StringRight("0" & @SEC, 2)
; ── 7. XML aufbauen ──────────────────────────────────────
Local $sX = ""
$sX &= '<?xml version="1.0" encoding="UTF-8"?>' & @CRLF
$sX &= '<!-- REB 23.003 (2009) - X31 Export AutoIt v5 -->' & @CRLF
$sX &= '<GAEB xmlns="http://www.gaeb.de/GAEB_DA_XML/DA31/3.3" xmlns:BVBS="BVBS">' & @CRLF
; GAEBInfo
$sX &= '<GAEBInfo>'
$sX &= '<Version>3.3</Version>'
$sX &= '<VersDate>2023-01</VersDate>'
$sX &= '<Date>' & $sISODatum & '</Date>'
$sX &= '<Time>' & $sTime & '</Time>'
$sX &= '<ProgSystem>AutoIt REB Engine V1.2</ProgSystem>'
$sX &= '<ProgName>AutoIt REB X31 Export</ProgName>'
$sX &= '</GAEBInfo>' & @CRLF
; QtyDeterm
$sX &= '<QtyDeterm>' & @CRLF
; PrjInfo
$sX &= '<PrjInfo>'
$sX &= '<RefPrjName>' & _XE($sBaustelle) & '</RefPrjName>'
$sX &= '<RefPrjID>' & _XE($sLVName) & '</RefPrjID>'
$sX &= '</PrjInfo>' & @CRLF
; QtyDetermInfo
$sX &= '<QtyDetermInfo ID="' & $sUID & '">'
$sX &= '<MethodDescription>REB23003-2009</MethodDescription>'
If $sStartISO <> "" Then $sX &= '<PeriodFrom>' & $sStartISO & '</PeriodFrom>'
If $sEndISO <> "" Then $sX &= '<PeriodTo>' & $sEndISO & '</PeriodTo>'
$sX &= '</QtyDetermInfo>' & @CRLF
; DP
$sX &= '<DP>31</DP>' & @CRLF
; OWN (leer, aber Pflichtfeld)
$sX &= '<OWN><Address><Name1>' & _XE($sAspaN) & '</Name1><Name2></Name2>'
$sX &= '<Name3/><Name4/><Street></Street><PCode></PCode><City></City>'
$sX &= '<Contact/><Phone>' & _XE($sAspaTel) & '</Phone><Fax/><Email/>'
$sX &= '</Address></OWN>' & @CRLF
; CTR (leer, aber Pflichtfeld)
$sX &= '<CTR><Address><Name1/><Name2></Name2><Name3/><Name4/>'
$sX &= '<Street></Street><PCode></PCode><City></City>'
$sX &= '<Contact/><Phone/><Fax/><Email/></Address></CTR>' & @CRLF
; BoQ
$sX &= '<BoQ ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<RefBoQName>' & _XE($sLVName) & '</RefBoQName>'
$sX &= '<RefBoQID>' & $sBoQUID & '</RefBoQID>' & @CRLF
; BoQBkdn exakt wie funktionierende alte Version
; WICHTIG: <Num>No</Num> und <LblBoQBkdn> sind Pflicht für MWM/Dataflor!
Select
Case $sOZTyp = "A"
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Titel</LblBoQBkdn><Length>' & $iMaxL1 & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</LblBoQBkdn><Length>' & $iMaxL2 & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Item</Type><LblBoQBkdn>Position</LblBoQBkdn><Length>' & $iMaxPos & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Index</Type><LblBoQBkdn>Index</LblBoQBkdn><Length>1</Length><Num>No</Num></BoQBkdn>'
Case $sOZTyp = "B"
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Titel</LblBoQBkdn><Length>' & $iMaxL1 & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</LblBoQBkdn><Length>' & $iMaxL2 & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Gewerk</LblBoQBkdn><Length>' & $iMaxL3 & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Item</Type><LblBoQBkdn>Position</LblBoQBkdn><Length>' & $iMaxPos & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Index</Type><LblBoQBkdn>Index</LblBoQBkdn><Length>1</Length><Num>No</Num></BoQBkdn>'
Case $sOZTyp = "C"
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Titel</LblBoQBkdn><Length>2</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Item</Type><LblBoQBkdn>Position</LblBoQBkdn><Length>' & $iMaxPos & '</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>Index</Type><LblBoQBkdn>Index</LblBoQBkdn><Length>1</Length><Num>No</Num></BoQBkdn>'
EndSelect
$sX &= @CRLF
; Ctlg (Pflichtfeld in alter Version)
$sX &= '<Ctlg><CtlgID>idDIN276_1993</CtlgID>'
$sX &= '<CtlgType>cost group DIN 276-93</CtlgType>'
$sX &= '<CtlgName>DIN 276-93</CtlgName></Ctlg>' & @CRLF
; BoQBody
$sX &= '<BoQBody>' & @CRLF
; OZ-Zähler für QTakeoff-Blattadressen
Local $iOZCnt = 0
; Typ C: Flachliste unter einer Sammelgruppe
If $sOZTyp = "C" Then
$sX &= '<BoQCtgy RNoPart="' & _XE($sBauabs) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody><Itemlist>' & @CRLF
For $i = 0 To $iPosN - 1
Local $aOZC = _OZInfo($aPosData[$i][0])
$sX &= _BuildItem($aPosData[$i], $aOZC[4], $iOZCnt)
Next
$sX &= '</Itemlist></BoQBody>' & @CRLF
$sX &= '</BoQCtgy>' & @CRLF
; Typ A: 2 Ebenen
ElseIf $sOZTyp = "A" Then
For $t = 0 To $iTitelN - 1
Local $sT = $aTitel[$t]
$sX &= '<BoQCtgy RNoPart="' & _XE($sT) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody>' & @CRLF
For $u = 0 To $iUTitelN - 1
If $aUTitel[$u][0] <> $sT Then ContinueLoop
Local $sU = $aUTitel[$u][1]
$sX &= '<BoQCtgy RNoPart="' & _XE($sU) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody><Itemlist>' & @CRLF
For $p = 0 To $iPosN - 1
Local $aOZA = _OZInfo($aPosData[$p][0])
If $aOZA[1] <> $sT Or $aOZA[2] <> $sU Then ContinueLoop
$sX &= _BuildItem($aPosData[$p], $aOZA[4], $iOZCnt)
Next
$sX &= '</Itemlist></BoQBody>' & @CRLF
$sX &= '</BoQCtgy>' & @CRLF
Next
$sX &= '</BoQBody>' & @CRLF
$sX &= '</BoQCtgy>' & @CRLF
Next
; Typ B: 3 Ebenen
ElseIf $sOZTyp = "B" Then
For $t = 0 To $iTitelN - 1
Local $sTB = $aTitel[$t]
$sX &= '<BoQCtgy RNoPart="' & _XE($sTB) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody>' & @CRLF
; Ebene 2
Local $aE2Seen[100]
Local $iE2N = 0
For $u = 0 To $iUTitelN - 1
If $aUTitel[$u][0] <> $sTB Then ContinueLoop
Local $sE2 = $aUTitel[$u][1]
; Ebene 2 nur einmal öffnen
Local $bE2New = True
For $x = 0 To $iE2N - 1
If $aE2Seen[$x] = $sE2 Then
$bE2New = False
ExitLoop
EndIf
Next
If $bE2New Then
If $iE2N > 0 Then
; Vorherige Ebene 2 schliessen
$sX &= '</BoQBody></BoQCtgy>' & @CRLF
EndIf
$aE2Seen[$iE2N] = $sE2
$iE2N += 1
$sX &= '<BoQCtgy RNoPart="' & _XE($sE2) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody>' & @CRLF
EndIf
; Ebene 3
Local $sE3 = $aUTitel[$u][2]
$sX &= '<BoQCtgy RNoPart="' & _XE($sE3) & '" ID="DF_' & _NextID() & '">' & @CRLF
$sX &= '<BoQBody><Itemlist>' & @CRLF
For $p = 0 To $iPosN - 1
Local $aOZB = _OZInfo($aPosData[$p][0])
If $aOZB[1] <> $sTB Or $aOZB[2] <> $sE2 Or $aOZB[3] <> $sE3 Then ContinueLoop
$sX &= _BuildItem($aPosData[$p], $aOZB[4], $iOZCnt)
Next
$sX &= '</Itemlist></BoQBody>' & @CRLF
$sX &= '</BoQCtgy>' & @CRLF
Next
If $iE2N > 0 Then
$sX &= '</BoQBody></BoQCtgy>' & @CRLF
EndIf
$sX &= '</BoQBody>' & @CRLF
$sX &= '</BoQCtgy>' & @CRLF
Next
EndIf
$sX &= '</BoQBody>' & @CRLF
$sX &= '</BoQ>' & @CRLF
$sX &= '</QtyDeterm>' & @CRLF
$sX &= '</GAEB>'
; ── 8. Als echtes UTF-8 mit BOM speichern ────────────────
; BOM = EF BB BF, dann UTF-8 Bytes
; StringToBinary($s, 4) = UTF-8 Kodierung
If FileExists($sAusgabeX31) Then FileDelete($sAusgabeX31)
Local $binBOM = Binary("0xEFBBBF")
Local $binBody = StringToBinary($sX, 4)
Local $binFull = $binBOM & $binBody
Local $bOK = FileWrite($sAusgabeX31, $binFull)
If Not $bOK Or Not FileExists($sAusgabeX31) Then
MsgBox(16, "Fehler", "Ausgabedatei konnte nicht geschrieben werden:" & @CRLF & $sAusgabeX31)
Return SetError(4, 0, False)
EndIf
MsgBox(64, "X31 Export", "X31-Datei erfolgreich erstellt!" & @CRLF & @CRLF & _
"Datei: " & $sAusgabeX31 & @CRLF & _
"Positionen: " & $iPosN & @CRLF & _
"OZ-Typ: " & $sOZTyp & @CRLF & _
"Format: GAEB DA XML 3.3 / DA31 / REB 23.003")
Return True
EndFunc
; ==============================================================================
; _BuildItem Einzelnes <Item>-Element erzeugen
; Exakt nach funktionierender alter Struktur:
; <Item>
; <QtyDeterm>
; <Qty>N.NNN</Qty>
; <QDetermItem>
; <QTakeoff Row="K-Zeile"/>
; <BVBS:Explanation>...</BVBS:Explanation>
; </QDetermItem>
; <QDetermItem>
; <QTakeoff Row="L-Zeile"/>
; </QDetermItem>
; </QtyDeterm>
; </Item>
; ==============================================================================
Func _BuildItem(ByRef $aPos, $sPosNr, ByRef $iOZCnt)
Local $fQty = $aPos[1]
Local $sOrt = $aPos[2]
Local $sBeschr = $aPos[3]
Local $sBemerk = $aPos[4]
Local $sFaktor = $aPos[7]
Local $sEinzel = $aPos[8]
; Qty formatieren (3 Dezimalstellen)
Local $sQty = _FmtQty($fQty)
; OZ-Codes für K- und L-Zeile
Local $sOZ1 = _REBOZCode($iOZCnt)
$iOZCnt += 1
Local $sOZ2 = _REBOZCode($iOZCnt)
$iOZCnt += 1
; Formel für L-Zeile: "100091" + Qty
; z.B. "1000912.771=" entspricht Formel 91 mit Wert 2.771
Local $sFormel = "100091" & $sQty
; K-Zeilen-Text: bevorzuge Bemerkung, dann Ort
Local $sKText = $sBemerk
If $sKText = "" Then $sKText = $sOrt
If $sKText = "" Then $sKText = $sBeschr
Local $sItem = ""
$sItem &= '<Item ID="DF_' & _NextID() & '" RNoPart="' & _XE($sPosNr) & '">'
$sItem &= '<QtyDeterm>'
$sItem &= '<Qty>' & $sQty & '</Qty>'
; K-Zeile (Kommentarzeile mit Bezeichnung)
$sItem &= '<QDetermItem>'
$sItem &= '<QTakeoff Row="' & _XE(_KZeile($sKText, $sOZ1)) & '"/>'
$sItem &= '<BVBS:Explanation>' & _XE(StringStripWS($sBeschr, 3)) & '</BVBS:Explanation>'
$sItem &= '</QDetermItem>'
; L-Zeile (Mengenzeile mit Formel 91)
$sItem &= '<QDetermItem>'
$sItem &= '<QTakeoff Row="' & _XE(_LZeile($sFormel, $sOZ2)) & '"/>'
$sItem &= '</QDetermItem>'
$sItem &= '</QtyDeterm>'
$sItem &= '</Item>' & @CRLF
Return $sItem
EndFunc
; ==============================================================================
; Hilfsfunktionen
; ==============================================================================
; K-Zeile: 12sp + * + Bezeichnung(56) + OZCode(6) + 5sp = 80 Zeichen
Func _KZeile($sBez, $sOZCode)
Local $sBezPad = StringLeft($sBez & " ", 56)
Return " *" & $sBezPad & $sOZCode & " "
EndFunc
; L-Zeile: 25sp + Formel"="(44) + OZCode(6) + 5sp = 80 Zeichen
Func _LZeile($sFormel, $sOZCode)
Local $sF = StringLeft($sFormel & "=" & " ", 44)
Return " " & $sF & $sOZCode & " "
EndFunc
; REB OZ-Code aus Zähler: 0-99 → "1000B0"-Format, dann Buchstaben
Func _REBOZCode($iIdx)
If $iIdx < 100 Then
Local $s = String($iIdx)
While StringLen($s) < 3
$s = "0" & $s
WEnd
Return "100" & $s
EndIf
Local $iLIdx = $iIdx - 100
Local $sABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Local $sLet = ""
Local $iWork = $iLIdx
Do
$sLet = StringMid($sABC, Mod($iWork, 26) + 1, 1) & $sLet
$iWork = Int($iWork / 26) - 1
Until $iWork < 0
If StringLen($sLet) > 2 Then $sLet = StringRight($sLet, 2)
Return "100" & $sLet & "0"
EndFunc
; Qty auf 3 Dezimalstellen formatieren
Func _FmtQty($fVal)
; Manuell formatieren ohne StringFormat
Local $iGanz = Int($fVal)
Local $fDez = $fVal - $iGanz
If $fDez < 0 Then $fDez = -$fDez
Local $iD3 = Int($fDez * 1000 + 0.5)
Local $sDez = String($iD3)
While StringLen($sDez) < 3
$sDez = "0" & $sDez
WEnd
Return String($iGanz) & "." & $sDez
EndFunc
; OZ analysieren
Func _OZInfo($sOZ)
Local $aI[10]
$aI[0] = "?"
$aI[1] = ""
$aI[2] = ""
$aI[3] = ""
$aI[4] = $sOZ
$aI[5] = $sOZ
$aI[6] = 0
$aI[7] = 0
$aI[8] = 0
$aI[9] = 0
If $sOZ = "" Then Return $aI
If StringRegExp($sOZ, "^\d{6,10}$") Then
$aI[0] = "C"
$aI[4] = $sOZ
$aI[9] = StringLen($sOZ)
Return $aI
EndIf
Local $aS = StringSplit($sOZ, ".", $STR_NOCOUNT)
Local $n = UBound($aS)
If $n = 3 Then
$aI[0] = "A"
$aI[1] = $aS[0]
$aI[6] = StringLen($aS[0])
$aI[2] = $aS[1]
$aI[7] = StringLen($aS[1])
$aI[4] = $aS[2]
$aI[9] = StringLen($aS[2])
ElseIf $n = 4 Then
$aI[0] = "B"
$aI[1] = $aS[0]
$aI[6] = StringLen($aS[0])
$aI[2] = $aS[1]
$aI[7] = StringLen($aS[1])
$aI[3] = $aS[2]
$aI[8] = StringLen($aS[2])
$aI[4] = $aS[3]
$aI[9] = StringLen($aS[3])
EndIf
$aI[5] = $sOZ
Return $aI
EndFunc
; OZ-Typ prüfen
Func _IsValidOZ($sOZ)
If $sOZ = "" Then Return False
If StringRegExp($sOZ, "^\d{1,4}\.\d{1,4}\.\d{1,6}$") Then Return True
If StringRegExp($sOZ, "^\d{1,4}\.\d{1,4}\.\d{1,4}\.\d{1,6}$") Then Return True
If StringRegExp($sOZ, "^\d{6,10}$") Then Return True
Return False
EndFunc
; Kopfdaten-Wert lesen
Func _KopfGet(ByRef $aKopf, $sKey)
For $i = 0 To UBound($aKopf) - 1
If $aKopf[$i][0] = $sKey Then Return $aKopf[$i][1]
Next
Return ""
EndFunc
; XML-Sonderzeichen kodieren
Func _XE($s)
$s = StringReplace($s, "&", "&amp;")
$s = StringReplace($s, "<", "&lt;")
$s = StringReplace($s, ">", "&gt;")
$s = StringReplace($s, '"', "&quot;")
$s = StringReplace($s, "'", "&apos;")
Return $s
EndFunc
; String zu Float (Komma→Punkt)
Func _ToFloat($s)
If $s = "" Then Return 0
Return Number(StringReplace(StringStripWS($s, 3), ",", "."))
EndFunc
; Datum DD.MM.YYYY → YYYY-MM-DD
Func _DatumISO($s)
$s = StringStripWS($s, 3)
If StringRegExp($s, "^\d{2}\.\d{2}\.\d{4}$") Then
Local $aD = StringSplit($s, ".", $STR_NOCOUNT)
Return $aD[2] & "-" & $aD[1] & "-" & $aD[0]
EndIf
Return $s
EndFunc
; Fortlaufende DF_-ID
Func _NextID()
$g_iIDCnt += 1
Return $g_iIDCnt
EndFunc
; Pseudo-UUID
Func _UUID()
Local $sHex = "0123456789abcdef"
Local $sUID = ""
Local $aLen[5]
$aLen[0] = 8
$aLen[1] = 4
$aLen[2] = 4
$aLen[3] = 4
$aLen[4] = 12
For $i = 0 To 4
If $i > 0 Then $sUID &= "-"
For $j = 1 To $aLen[$i]
$sUID &= StringMid($sHex, Random(1, 16, 1), 1)
Next
Next
Return $sUID
EndFunc