Files
aufmass-web/_legacy/includes/x31_txt2Dataflor.au3
T

651 lines
24 KiB
AutoIt
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; ==============================================================================
; _X31_Export Hauptfunktion
; ==============================================================================
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 $sISODatum = _DatumISO($sDatum)
Local $sUID = _UUID()
Local $sBoQUID = _UUID()
Local $sLVName = ($sVertrag <> "") ? $sVertrag : ($sBaustelle & " " & $sBauabs)
; Feldlängen auf Dataflor-Limits kürzen
Local $sLVName20 = StringLeft($sLVName, 20)
Local $sBaust50 = StringLeft($sBaustelle, 50)
; ── 3. Aufmassdaten parsen ───────────────────────────────
; Spalten (0-basiert nach Split mit |):
; 0=Ort, 1=OZ, 2=Faktor, 3=Einzelmenge, 4,5=leer, 6=GesamtMenge,
; 7=Einheit, 8=Beschreibung, 9=Bemerkung, 10=Menge2, 11=EP, 12=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 > 10) ? StringStripWS($aS[10], 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: Spalte 6 (Gesamtmenge) hat immer Vorrang sie ist bereits mit Faktor berechnet.
; Faktor * Einzelmenge wird NICHT selbst gerechnet, da Spalte 6 das Ergebnis enthält.
; Nur wenn Spalte 6 leer/0: Einzelmenge als Fallback, dann Faktor.
Local $fFaktor = _ToFloat($sFaktor)
Local $fEinzel = _ToFloat($sEinzel)
Local $fGesamt = _ToFloat($sGesamt)
Local $fQty = $fGesamt
;~ If $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 ───────────────────────────────
; KORREKTUR: Dataflor-kompatible Lengths aus dem echten Dataflor-Export
; Für Typ B (X.X.XX.XXXX):
; Titel(E1): Length=1 → '1', '2', '3', ...
; Bauteil(E2): Length=1 → '2', '3', '4', ... (NICHT 2!)
; Abschnitt(E3): Length=2 → '01', '02', '03', ... (NICHT 3!)
; Position: Length=4 → '0470', '0010', ...
; Für Typ A (XX.XX.XXXX):
; Titel(E1): Length=2
; Bauteil(E2): Length=2
; Position: Length=4
Local $sOZTyp = "A"
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[9] > $iMaxPos Then $iMaxPos = $aOZ[9]
Next
; ── 5. OZ-Hierarchie aufbauen ────────────────────────────
; (wie v5, unverändert)
; ── 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 v8 (Dataflor-kompatibel) -->' & @CRLF
$sX &= '<GAEB xmlns="http://www.gaeb.de/GAEB_DA_XML/DA31/3.3" xmlns:BVBS="BVBS">'
; GAEBInfo (kompakt, kein CRLF zwischen Tags - wie Dataflor-Referenz)
$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>'
; QtyDeterm
$sX &= '<QtyDeterm>'
; PrjInfo
$sX &= '<PrjInfo>'
$sX &= '<RefPrjName>' & _XE($sBaust50) & '</RefPrjName>'
$sX &= '<RefPrjID>' & _XE($sLVName20) & '</RefPrjID>'
$sX &= '</PrjInfo>'
; QtyDetermInfo
$sX &= '<QtyDetermInfo ID="' & $sUID & '">'
$sX &= '<MethodDescription>REB23003-2009</MethodDescription>'
$sX &= '</QtyDetermInfo>'
; DP
$sX &= '<DP>31</DP>'
; OWN
$sX &= '<OWN><Address>'
$sX &= '<Name1>' & _XE($sAspaN) & '</Name1>'
$sX &= '<Name2></Name2><Name3/><Name4/>'
$sX &= '<Street></Street><PCode></PCode><City></City>'
$sX &= '<Contact/><Phone>' & _XE($sAspaTel) & '</Phone><Fax/><Email/>'
$sX &= '</Address></OWN>'
; CTR
$sX &= '<CTR><Address><Name1/><Name2></Name2><Name3/><Name4/>'
$sX &= '<Street></Street><PCode></PCode><City></City>'
$sX &= '<Contact/><Phone/><Fax/><Email/></Address></CTR>'
; BoQ
$sX &= '<BoQ ID="DF_' & _NextID() & '">'
$sX &= '<RefBoQName>' & _XE($sLVName20) & '</RefBoQName>'
$sX &= '<RefBoQID>' & $sBoQUID & '</RefBoQID>'
; ─────────────────────────────────────────────────────────────────────
; BoQBkdn KORRIGIERT für Dataflor-Kompatibilität
; Dataflor liest die OZ-Segmentlängen exakt aus diesen Length-Feldern.
; Falsche Werte → Import wird lautlos abgebrochen!
;
; Typ B (X.X.XX.XXXX) → Dataflor-Referenz-Schema:
; Titel Length=1 (E1: 1 Stelle)
; Bauteil Length=1 (E2: 1 Stelle, z.B. '3')
; Abschnitt Length=2 (E3: 2 Stellen, z.B. '01')
; Position Length=4 (Pos: 4 Stellen, z.B. '0470')
; Index Length=1 (immer)
;
; Typ A (XX.XX.XXXX):
; Titel Length=2 (E1)
; Bauteil Length=2 (E2)
; Position Length=4
; Index Length=1
; ─────────────────────────────────────────────────────────────────────
Select
Case $sOZTyp = "A"
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Titel</LblBoQBkdn><Length>2</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</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>'
Case $sOZTyp = "B"
; Dataflor-Referenz (verifiziert): Titel L=1, Bauteil L=1, Abschnitt L=2
; Labels exakt wie Dataflor-Export: 'Titel', 'Bauteil', 'Abschnitt'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Titel</LblBoQBkdn><Length>1</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</LblBoQBkdn><Length>1</Length><Num>No</Num></BoQBkdn>'
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Abschnitt</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>'
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
; Ctlg
$sX &= '<Ctlg><CtlgID>idDIN276_1993</CtlgID>'
$sX &= '<CtlgType>cost group DIN 276-93</CtlgType>'
$sX &= '<CtlgName>DIN 276-93</CtlgName></Ctlg>'
; BoQBody
$sX &= '<BoQBody>'
; OZ-Zähler für QTakeoff-Blattadressen
Local $iOZCnt = 0
; Eindeutige OZ in TXT-Reihenfolge sammeln
Local $aOZOrder[10000][4]
Local $iOZOrderN = 0
For $i = 0 To $iPosN - 1
Local $aOZi = _OZInfo($aPosData[$i][0])
Local $bFound = False
For $j = 0 To $iOZOrderN - 1
If $aOZOrder[$j][0] = $aOZi[1] And $aOZOrder[$j][1] = $aOZi[2] And _
$aOZOrder[$j][2] = $aOZi[3] And $aOZOrder[$j][3] = $aOZi[4] Then
$bFound = True
ExitLoop
EndIf
Next
If Not $bFound Then
$aOZOrder[$iOZOrderN][0] = $aOZi[1]
$aOZOrder[$iOZOrderN][1] = $aOZi[2]
$aOZOrder[$iOZOrderN][2] = $aOZi[3]
$aOZOrder[$iOZOrderN][3] = $aOZi[4]
$iOZOrderN += 1
EndIf
Next
; Items ausgeben hierarchische Struktur
Local $sCurE1 = ""
Local $sCurE2 = ""
Local $sCurE3 = ""
Local $bIL = False
For $oi = 0 To $iOZOrderN - 1
Local $sE1 = $aOZOrder[$oi][0]
Local $sE2 = $aOZOrder[$oi][1]
Local $sE3 = $aOZOrder[$oi][2]
Local $sPos = $aOZOrder[$oi][3]
Local $bSameE1 = ($sE1 = $sCurE1)
Local $bSameE2 = ($sE2 = $sCurE2) And $bSameE1
Local $bSameE3 = ($sE3 = $sCurE3) And $bSameE2
; Schließen bei Wechsel
If $sOZTyp = "B" And Not $bSameE3 And $bIL Then
$sX &= '</Itemlist></BoQBody>'
$sX &= '</BoQCtgy>'
$bIL = False
$sCurE3 = ""
EndIf
If Not $bSameE2 And $bIL Then
$sX &= '</Itemlist></BoQBody>'
$sX &= '</BoQCtgy>'
$bIL = False
$sCurE3 = ""
EndIf
If $sOZTyp = "B" And Not $bSameE2 And $sCurE2 <> "" Then
$sX &= '</BoQBody>'
$sX &= '</BoQCtgy>'
$sCurE2 = ""
$sCurE3 = ""
EndIf
If Not $bSameE1 And $sCurE1 <> "" Then
$sX &= '</BoQBody>'
$sX &= '</BoQCtgy>'
$sCurE1 = ""
EndIf
; Öffnen
If $sE1 <> $sCurE1 Then
$sX &= '<BoQCtgy RNoPart="' & _XE($sE1) & '" ID="DF_' & _NextID() & '">'
$sX &= '<BoQBody>'
$sCurE1 = $sE1
EndIf
If $sOZTyp = "B" And $sE2 <> $sCurE2 Then
$sX &= '<BoQCtgy RNoPart="' & _XE($sE2) & '" ID="DF_' & _NextID() & '">'
$sX &= '<BoQBody>'
$sCurE2 = $sE2
$sCurE3 = ""
EndIf
If $sOZTyp <> "B" Then $sCurE2 = $sE2
If $sOZTyp = "B" Then $sCurE3 = $sE3
If Not $bIL Then
If $sOZTyp = "B" Then
$sX &= '<BoQCtgy RNoPart="' & _XE($sCurE3) & '" ID="DF_' & _NextID() & '">'
Else
$sX &= '<BoQCtgy RNoPart="' & _XE($sCurE2) & '" ID="DF_' & _NextID() & '">'
EndIf
$sX &= '<BoQBody><Itemlist>'
$bIL = True
EndIf
; Gesamtmenge dieser Position summieren
Local $fQtySum = 0
For $i = 0 To $iPosN - 1
Local $aOZck = _OZInfo($aPosData[$i][0])
If $aOZck[1] <> $sE1 Or $aOZck[2] <> $sE2 Then ContinueLoop
If $sOZTyp = "B" And $aOZck[3] <> $sE3 Then ContinueLoop
If $aOZck[4] <> $sPos Then ContinueLoop
$fQtySum += $aPosData[$i][1]
Next
; Item schreiben
$sX &= '<Item ID="DF_' & _NextID() & '" RNoPart="' & _XE($sPos) & '">'
$sX &= '<QtyDeterm>'
; Qty mit Punkt als Dezimaltrenner (XML-Standard)
$sX &= '<Qty>' & StringReplace(_FmtQty($fQtySum), ",", ".") & '</Qty>'
; Alle Ansätze dieser Position ausgeben
For $i = 0 To $iPosN - 1
Local $aOZan = _OZInfo($aPosData[$i][0])
If $aOZan[1] <> $sE1 Or $aOZan[2] <> $sE2 Then ContinueLoop
If $sOZTyp = "B" And $aOZan[3] <> $sE3 Then ContinueLoop
If $aOZan[4] <> $sPos Then ContinueLoop
; KORREKTUR: _MakeQDetermPair mit BVBS:Explanation
$sX &= _MakeQDetermPair($aPosData[$i][2], $aPosData[$i][1], $iOZCnt)
Next
$sX &= '</QtyDeterm>'
$sX &= '</Item>'
Next
; Alle offenen Tags schließen
If $bIL Then
$sX &= '</Itemlist></BoQBody>'
$sX &= '</BoQCtgy>'
EndIf
If $sOZTyp = "B" And $sCurE2 <> "" Then
$sX &= '</BoQBody>'
$sX &= '</BoQCtgy>'
EndIf
If $sCurE1 <> "" Then
$sX &= '</BoQBody>'
$sX &= '</BoQCtgy>'
EndIf
$sX &= '</BoQBody>'
$sX &= '</BoQ>'
$sX &= '</QtyDeterm>'
$sX &= '</GAEB>'
; ── 8. Als echtes UTF-8 mit BOM speichern ────────────────
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 - Dataflor-kompatibel")
Return True
EndFunc
; ==============================================================================
; _MakeQDetermPair($sOrt, $fQty, $iOZCnt)
; Erzeugt EIN K+L-Zeilenpaar als zwei QDetermItem-Blöcke
;
; REIHENFOLGE: K-Zeile (Ort) zuerst, dann L-Zeile (Menge)
; QDetermItem { QTakeoff Row="*Ort..."
; BVBS:Explanation "Ort" } ← K-Zeile zuerst
; QDetermItem { QTakeoff Row="Menge..." } ← L-Zeile danach
; ==============================================================================
Func _MakeQDetermPair($sOrt, $fQty, ByRef $iOZCnt)
Local $sQty = _FmtQty($fQty)
Local $sOZ1 = _REBOZCode($iOZCnt) ; OZ1 = K-Zeile
$iOZCnt += 1
Local $sOZ2 = _REBOZCode($iOZCnt) ; OZ2 = L-Zeile
$iOZCnt += 1
Local $sOut = ""
; 1. K-Zeile (Ortsbezeichnung) + BVBS:Explanation ZUERST
$sOut &= '<QDetermItem>'
$sOut &= '<QTakeoff Row="' & _XE(_KZeile($sOrt, $sOZ1)) & '"/>'
$sOut &= '<BVBS:Explanation>' & _XE(StringStripWS($sOrt, 3)) & '</BVBS:Explanation>'
$sOut &= '</QDetermItem>'
; 2. L-Zeile (Mengenzeile Formel 91) DANACH
$sOut &= '<QDetermItem>'
$sOut &= '<QTakeoff Row="' & _XE(_LZeile($sQty, $sOZ2)) & '"/>'
$sOut &= '</QDetermItem>'
Return $sOut
EndFunc
; ==============================================================================
; Hilfsfunktionen
; ==============================================================================
; K-Zeile: 12 Leerzeichen + '*' + Ort(56 Zeichen padded) + OZCode(6) + 5 Leerzeichen = 80 Zeichen
Func _KZeile($sOrt, $sOZCode)
Local $sOrtPad = StringLeft($sOrt & " ", 56)
Return " *" & $sOrtPad & $sOZCode & " "
EndFunc
; L-Zeile: 25 Leerzeichen + '100091' + Menge + '=' + (Padding auf 44) + OZCode(6) + 5 Leerzeichen = 80 Zeichen
; Menge mit Komma (DA11-Standard), kein Faktor-Parameter (Menge ist bereits die Gesamtmenge)
Func _LZeile($sMenge, $sOZCode)
Local $sMengeK = StringReplace(StringStripWS($sMenge, 3), ".", ",")
Local $sFormel = "100091" & $sMengeK & "="
While StringLen($sFormel) < 44
$sFormel &= " "
WEnd
$sFormel = StringLeft($sFormel, 44)
Return " " & $sFormel & $sOZCode & " "
EndFunc
; REB Blattadresse aus Zähler (z.B. 0→"1000A0", 25→"1000Z0", 26→"1001A0")
Func _REBOZCode($iIdx)
Local $iBlatt = 1000 + Int($iIdx / 26)
Local $iZeile = Mod($iIdx, 26)
Local $sZeile = Chr(65 + $iZeile)
Return String($iBlatt) & $sZeile & "0"
EndFunc
; Qty formatieren: Komma als Dezimaltrenner, 3 Dezimalstellen (trailing zeros entfernt)
Func _FmtQty($fVal)
Local $fAbs = $fVal
If $fAbs < 0 Then $fAbs = -$fAbs
Local $iGanz = Int($fAbs)
Local $fDez = $fAbs - $iGanz
Local $iD3 = Int($fDez * 1000 + 0.5)
If $iD3 >= 1000 Then
$iGanz += 1
$iD3 = 0
EndIf
Local $sVorz = ""
If $fVal < 0 Then $sVorz = "-"
If $iD3 = 0 Then
Return $sVorz & String($iGanz)
EndIf
Local $sDez = String($iD3)
While StringLen($sDez) < 3
$sDez = "0" & $sDez
WEnd
While StringRight($sDez, 1) = "0" And StringLen($sDez) > 1
$sDez = StringLeft($sDez, StringLen($sDez) - 1)
WEnd
Return $sVorz & String($iGanz) & "," & $sDez
EndFunc
; OZ analysieren: Gibt Array zurück
; [0]=Typ(A/B/C), [1]=E1, [2]=E2, [3]=E3, [4]=Pos, [5]=OZOriginal
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
$s = StringStripWS($s, 3)
While StringRight($s, 1) = "," Or StringRight($s, 1) = "."
$s = StringLeft($s, StringLen($s) - 1)
WEnd
$s = StringReplace($s, ",", ".")
Return Number($s)
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