866 lines
30 KiB
AutoIt
866 lines
30 KiB
AutoIt
#include <Array.au3>
|
||
#include <File.au3>
|
||
#include <String.au3>
|
||
#include <Date.au3>
|
||
#include <Debug.au3>
|
||
#include <Excel.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"
|
||
|
||
|
||
; Globaler ID-Zähler für XML-IDs (muss vor Func-Aufrufen deklariert sein)
|
||
; Globaler ID-Zaehler (vor allen Local-Deklarationen)
|
||
Global $g_iIDCnt = 1000000
|
||
|
||
|
||
; ── Einstiegspunkt ──────────────────────────────────────────
|
||
Local $importPfadTxt = @ScriptDir & "\..\Meckenbeueren_AZ10_.txt"
|
||
Local $ausgabePfadX31 = @ScriptDir & "\..\Meckenbeueren_AZ10_x31neu.x31"
|
||
|
||
Local $exportExcelCalifornia = @ScriptDir & "\..\Meckenbeueren_AZ10_.xlsx"
|
||
|
||
;~ Local $importPfadTxt = @ScriptDir & "\..\FTTx_Achberg_AZ001_.txt"
|
||
;~ Local $ausgabePfadX31 = @ScriptDir & "\..\FTTx_Achberg_AZ001_x31neu.x31"
|
||
|
||
; Beispiel Aufruf
|
||
_REB_TXT_to_Excel($importPfadTxt,$exportExcelCalifornia)
|
||
|
||
|
||
;~ _X31_Export($importPfadTxt, $ausgabePfadX31)
|
||
|
||
|
||
|
||
Func _replaceKom2Punkt($sString)
|
||
Return StringReplace($sString, ",", ".")
|
||
EndFunc ;==>_replaceKom2Punkt
|
||
|
||
Func _replacePunkt2Koma($sString)
|
||
Return StringReplace($sString, ".", ",")
|
||
EndFunc ;==>_replacePunkt2Koma
|
||
|
||
|
||
Func _REB_TXT_to_Excel($sTXT, $sExcel)
|
||
|
||
Local $aLines
|
||
_FileReadToArray($sTXT, $aLines)
|
||
|
||
Local $Baustelle = ""
|
||
Local $Datum = ""
|
||
Local $Abschnitt = ""
|
||
|
||
Local $inHeader = False
|
||
Local $inData = False
|
||
|
||
; Kopf auslesen
|
||
For $i = 1 To $aLines[0]
|
||
|
||
Local $line = $aLines[$i]
|
||
|
||
If StringInStr($line, "[Kopfdaten]") Then
|
||
$inHeader = True
|
||
ContinueLoop
|
||
EndIf
|
||
|
||
If StringInStr($line, "[Aufmaßdaten]") Then
|
||
$inHeader = False
|
||
$inData = True
|
||
ExitLoop
|
||
EndIf
|
||
|
||
If $inHeader Then
|
||
|
||
If StringLeft($line, 9) = "Baustelle" Then
|
||
$Baustelle = StringTrimLeft($line, 10)
|
||
EndIf
|
||
|
||
If StringLeft($line, 5) = "Datum" Then
|
||
$Datum = StringTrimLeft($line, 6)
|
||
EndIf
|
||
|
||
If StringLeft($line, 13) = "Bauabschnitt=" Then
|
||
$Abschnitt = StringTrimLeft($line, 13)
|
||
EndIf
|
||
|
||
EndIf
|
||
|
||
Next
|
||
|
||
; Excel starten
|
||
Local $oExcel = _Excel_Open()
|
||
Local $oBook = _Excel_BookNew($oExcel)
|
||
|
||
; Kopf schreiben
|
||
_Excel_RangeWrite($oBook, Default, "Bauvorhaben", "A2")
|
||
_Excel_RangeWrite($oBook, Default, $Baustelle, "C2")
|
||
|
||
_Excel_RangeWrite($oBook, Default, "Leistungsverzeichnis", "A3")
|
||
_Excel_RangeWrite($oBook, Default, $Abschnitt, "C3")
|
||
|
||
_Excel_RangeWrite($oBook, Default, "Aufmaß", "A4")
|
||
_Excel_RangeWrite($oBook, Default, $Datum, "D4")
|
||
|
||
; Spaltenüberschrift
|
||
;~ _Excel_RangeWrite($oBook, Default, "OZ", "A7")
|
||
;~ _Excel_RangeWrite($oBook, Default, "Position", "B7")
|
||
;~ _Excel_RangeWrite($oBook, Default, "Abschnitt", "C7")
|
||
;~ _Excel_RangeWrite($oBook, Default, "Faktor", "D7")
|
||
;~ _Excel_RangeWrite($oBook, Default, "REB Formel", "E7")
|
||
;~ _Excel_RangeWrite($oBook, Default, "Ergebnis", "F7")
|
||
|
||
Local $row = 7
|
||
$inData = False
|
||
|
||
For $i = 1 To $aLines[0]
|
||
|
||
Local $line = $aLines[$i]
|
||
|
||
If StringInStr($line, "[Aufmaßdaten]") Then
|
||
$inData = True
|
||
ContinueLoop
|
||
EndIf
|
||
|
||
If $inData = False Then ContinueLoop
|
||
If StringStripWS($line, 8) = "" Then ContinueLoop
|
||
|
||
Local $f = StringSplit($line, "|")
|
||
|
||
If $f[0] < 10 Then ContinueLoop
|
||
|
||
Local $Ort = $f[1]
|
||
Local $OZ = $f[2]
|
||
Local $Formel = $f[3]
|
||
Local $Ergebnis = $f[7]
|
||
Local $Text = $f[9]
|
||
|
||
; Zeile 1 (Bemerkung)
|
||
_Excel_RangeWrite($oBook, Default, $OZ, "A" & $row)
|
||
_Excel_RangeWrite($oBook, Default, $Text, "B" & $row)
|
||
_Excel_RangeWrite($oBook, Default, $Ort, "C" & $row)
|
||
|
||
$row += 1
|
||
|
||
; Zeile 2 (Formel)
|
||
_Excel_RangeWrite($oBook, Default, $OZ, "A" & $row)
|
||
_Excel_RangeWrite($oBook, Default, "", "B" & $row)
|
||
_Excel_RangeWrite($oBook, Default, "", "C" & $row)
|
||
_Excel_RangeWrite($oBook, Default, $Formel, "D" & $row)
|
||
_Excel_RangeWrite($oBook, Default, $Formel, "E" & $row)
|
||
_Excel_RangeWrite($oBook, Default, $Ergebnis, "F" & $row)
|
||
|
||
$row += 1
|
||
|
||
Next
|
||
|
||
_Excel_BookSaveAs($oBook, $sExcel)
|
||
_Excel_Close($oExcel)
|
||
EndFunc
|
||
|
||
|
||
|
||
|
||
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)
|
||
|
||
; Feldlängen auf Dataflor-Limits kürzen
|
||
; RefBoQName / RefPrjID: max. 20 Zeichen
|
||
; RefPrjName / Name1: max. 50 Zeichen
|
||
Local $sLVName20 = StringLeft($sLVName, 20)
|
||
Local $sBaust50 = StringLeft($sBaustelle, 50)
|
||
|
||
; ── 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 ───────────────────────────────
|
||
; Typ bestimmen und OZ-Typ-spezifische Standard-Lengths setzen
|
||
; WICHTIG: Length = Feldbreite laut OZ-Maske, NICHT max. Zeichenlänge des Werts!
|
||
; Dataflor liest die Length aus BoQBkdn und ordnet OZ-Segmente zu.
|
||
; Falsche Lengths = kein Import (Segmente passen nicht ins Feld).
|
||
;
|
||
; Standard-Lengths (aus D11-OZ-Masken abgeleitet):
|
||
; Typ A (XX.XX.XXXX): L1=2, L2=2, L3=4 (Maske: 22PPPPi)
|
||
; Typ B (X.X.XX.XXXX): L1=1, L2=2, L3=3, L4=4 (Maske: 123PPPi)
|
||
; Typ C (XXXXXXXX): L1=8 (Maske: PPPPPPPPi)
|
||
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
|
||
|
||
; Feste Standard-Lengths je Typ (aus D11-OZ-Maske)
|
||
Local $iL1 = 2 ; Typ A/C Standard
|
||
Local $iL2 = 2
|
||
Local $iL3 = 2
|
||
Select
|
||
Case $sOZTyp = "A"
|
||
$iL1 = 2 ; z.B. '01'
|
||
$iL2 = 2 ; z.B. '03'
|
||
; $iMaxPos bereits 4 für '0008'
|
||
Case $sOZTyp = "B"
|
||
$iL1 = 1 ; z.B. '1' (1 Stelle)
|
||
$iL2 = 2 ; z.B. '3' (Feld 2-stellig laut Maske)
|
||
$iL3 = 3 ; z.B. '01' (Feld 3-stellig laut Maske '123PPPi')
|
||
; $iMaxPos bereits 4 für '0470'
|
||
Case $sOZTyp = "C"
|
||
; keine Ebenen, nur Item
|
||
EndSelect
|
||
|
||
; ── 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($sBaust50) & '</RefPrjName>'
|
||
$sX &= '<RefPrjID>' & _XE($sLVName20) & '</RefPrjID>'
|
||
$sX &= '</PrjInfo>' & @CRLF
|
||
|
||
; QtyDetermInfo – NUR MethodDescription laut GAEB DA XML 3.3 Schema!
|
||
; PeriodFrom/PeriodTo sind in QtyDetermInfo NICHT erlaubt → Dataflor-Warnung
|
||
$sX &= '<QtyDetermInfo ID="' & $sUID & '">'
|
||
$sX &= '<MethodDescription>REB23003-2009</MethodDescription>'
|
||
$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($sLVName20) & '</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>' & $iL1 & '</Length><Num>No</Num></BoQBkdn>'
|
||
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</LblBoQBkdn><Length>' & $iL2 & '</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>' & $iL1 & '</Length><Num>No</Num></BoQBkdn>'
|
||
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Bauteil</LblBoQBkdn><Length>' & $iL2 & '</Length><Num>No</Num></BoQBkdn>'
|
||
$sX &= '<BoQBkdn><Type>BoQLevel</Type><LblBoQBkdn>Gewerk</LblBoQBkdn><Length>' & $iL3 & '</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($aOZC[4], $aPosData[$i][1], $aPosData[$i][2], $aPosData[$i][3], $aPosData[$i][4], $iOZCnt, $aPosData[$i][7], $aPosData[$i][8])
|
||
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($aOZA[4], $aPosData[$p][1], $aPosData[$p][2], $aPosData[$p][3], $aPosData[$p][4], $iOZCnt, $aPosData[$p][7], $aPosData[$p][8])
|
||
Next
|
||
|
||
$sX &= '</Itemlist></BoQBody>' & @CRLF
|
||
$sX &= '</BoQCtgy>' & @CRLF
|
||
Next
|
||
|
||
$sX &= '</BoQBody>' & @CRLF
|
||
$sX &= '</BoQCtgy>' & @CRLF
|
||
Next
|
||
|
||
; Typ B: 3 Ebenen (E1.E2.E3.Pos)
|
||
ElseIf $sOZTyp = "B" Then
|
||
|
||
; Eindeutige E1-Werte (bereits in $aTitel)
|
||
For $t = 0 To $iTitelN - 1
|
||
Local $sTB = $aTitel[$t]
|
||
$sX &= '<BoQCtgy RNoPart="' & _XE($sTB) & '" ID="DF_' & _NextID() & '">' & @CRLF
|
||
$sX &= '<BoQBody>' & @CRLF
|
||
|
||
; Eindeutige E2-Werte unter diesem E1
|
||
Local $aE2List[100]
|
||
Local $iE2Cnt = 0
|
||
For $u = 0 To $iUTitelN - 1
|
||
If $aUTitel[$u][0] <> $sTB Then ContinueLoop
|
||
Local $sE2Chk = $aUTitel[$u][1]
|
||
Local $bDup = False
|
||
For $x = 0 To $iE2Cnt - 1
|
||
If $aE2List[$x] = $sE2Chk Then
|
||
$bDup = True
|
||
ExitLoop
|
||
EndIf
|
||
Next
|
||
If Not $bDup Then
|
||
$aE2List[$iE2Cnt] = $sE2Chk
|
||
$iE2Cnt += 1
|
||
EndIf
|
||
Next
|
||
|
||
; Für jedes E2 unter diesem E1
|
||
For $e2i = 0 To $iE2Cnt - 1
|
||
Local $sCurE2 = $aE2List[$e2i]
|
||
$sX &= '<BoQCtgy RNoPart="' & _XE($sCurE2) & '" ID="DF_' & _NextID() & '">' & @CRLF
|
||
$sX &= '<BoQBody>' & @CRLF
|
||
|
||
; Alle E3-Werte unter diesem E1.E2
|
||
For $u = 0 To $iUTitelN - 1
|
||
If $aUTitel[$u][0] <> $sTB Then ContinueLoop
|
||
If $aUTitel[$u][1] <> $sCurE2 Then ContinueLoop
|
||
Local $sCurE3 = $aUTitel[$u][2]
|
||
|
||
$sX &= '<BoQCtgy RNoPart="' & _XE($sCurE3) & '" ID="DF_' & _NextID() & '">' & @CRLF
|
||
$sX &= '<BoQBody><Itemlist>' & @CRLF
|
||
|
||
; Positionen unter diesem E1.E2.E3
|
||
For $p = 0 To $iPosN - 1
|
||
Local $aOZB = _OZInfo($aPosData[$p][0])
|
||
If $aOZB[1] <> $sTB Or $aOZB[2] <> $sCurE2 Or $aOZB[3] <> $sCurE3 Then ContinueLoop
|
||
$sX &= _BuildItem($aOZB[4], $aPosData[$p][1], $aPosData[$p][2], $aPosData[$p][3], $aPosData[$p][4], $iOZCnt, $aPosData[$p][7], $aPosData[$p][8])
|
||
Next
|
||
|
||
$sX &= '</Itemlist></BoQBody>' & @CRLF
|
||
$sX &= '</BoQCtgy>' & @CRLF
|
||
Next
|
||
|
||
$sX &= '</BoQBody>' & @CRLF
|
||
$sX &= '</BoQCtgy>' & @CRLF
|
||
Next
|
||
|
||
$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
|
||
; Struktur in Dataflor/MWM:
|
||
; K-Zeile (*) = RSA-Abschnitt/Ort
|
||
; L-Zeile = Formel 91 mit Faktor und Einzelmenge getrennt
|
||
; Parameter werden als Einzelwerte übergeben (kein 2D-Array-Slice in AutoIt!)
|
||
; ==============================================================================
|
||
Func _BuildItem($sPosNr, $fQty, $sOrt, $sBeschr, $sBemerk, ByRef $iOZCnt, $sFaktor = "1", $sEinzel = "")
|
||
|
||
; Qty für XML (<Qty> Tag): Punkt als Dezimaltrenner
|
||
Local $sQtyXML = StringReplace(_FmtQty($fQty), ",", ".")
|
||
; Qty für DA11 L-Zeile: Komma, direkt die berechnete Gesamtmenge
|
||
Local $sQty = _FmtQty($fQty)
|
||
|
||
; L-Zeilen-Menge = berechnetes Qty (Faktor bereits eingerechnet)
|
||
; Exakt wie Dataflor: "100091" + Gesamtmenge + "="
|
||
Local $sMenge = $sQty
|
||
|
||
; Faktor bereinigen
|
||
Local $sFakClean = StringStripWS($sFaktor, 3)
|
||
If $sFakClean = "" Or $sFakClean = "0" Then $sFakClean = "1"
|
||
$sFakClean = StringReplace($sFakClean, ".", ",")
|
||
|
||
; OZ-Codes für K- und L-Zeile
|
||
Local $sOZ1 = _REBOZCode($iOZCnt)
|
||
$iOZCnt += 1
|
||
Local $sOZ2 = _REBOZCode($iOZCnt)
|
||
$iOZCnt += 1
|
||
|
||
; K-Zeilen-Text = RSA-Abschnitt (Ort), Fallback auf Bemerkung
|
||
Local $sKText = $sOrt
|
||
If $sKText = "" Then $sKText = $sBemerk
|
||
If $sKText = "" Then $sKText = $sBeschr
|
||
|
||
Local $sItem = ""
|
||
$sItem &= '<Item ID="DF_' & _NextID() & '" RNoPart="' & _XE($sPosNr) & '">'
|
||
$sItem &= '<QtyDeterm>'
|
||
$sItem &= '<Qty>' & $sQtyXML & '</Qty>'
|
||
|
||
; K-Zeile: RSA-Abschnitt als Kommentarzeile
|
||
$sItem &= '<QDetermItem>'
|
||
$sItem &= '<QTakeoff Row="' & _XE(_KZeile($sKText, $sOZ1)) & '"/>'
|
||
$sItem &= '<BVBS:Explanation>' & _XE(StringStripWS($sBeschr, 3)) & '</BVBS:Explanation>'
|
||
$sItem &= '</QDetermItem>'
|
||
|
||
; L-Zeile: Formel 91 mit Faktor und Menge getrennt
|
||
$sItem &= '<QDetermItem>'
|
||
$sItem &= '<QTakeoff Row="' & _XE(_LZeile($sFakClean, $sMenge, $sOZ2)) & '"/>'
|
||
$sItem &= '</QDetermItem>'
|
||
|
||
$sItem &= '</QtyDeterm>'
|
||
$sItem &= '</Item>' & @CRLF
|
||
Return $sItem
|
||
EndFunc
|
||
|
||
|
||
; ==============================================================================
|
||
; Hilfsfunktionen
|
||
; ==============================================================================
|
||
|
||
; K-Zeile: 13sp(*) + Ort(56 Zeichen) + OZCode(6) + 5sp = 80 Zeichen
|
||
Func _KZeile($sOrt, $sOZCode)
|
||
Local $sOrtPad = StringLeft($sOrt & " ", 56)
|
||
Return " *" & $sOrtPad & $sOZCode & " "
|
||
EndFunc
|
||
|
||
; L-Zeile: 25sp + "100091" + Menge + "=" + padding auf 44 + BlatAdr(6) + 5sp = 80 Zeichen
|
||
; Exakt wie Dataflor-Export: " 100091Menge= BlatAdr "
|
||
; Faktor wird als Multiplikator in die Menge eingerechnet (qty = faktor * einzelmenge)
|
||
; Der Faktor ist NICHT separat in der Zeile – Dataflor rechnet über die Qty
|
||
Func _LZeile($sFaktor, $sMenge, $sOZCode)
|
||
; Menge mit Komma (DA11-Standard), Punkt entfernen
|
||
Local $sMengeK = StringReplace(StringStripWS($sMenge, 3), ".", ",")
|
||
; Formel: direkt "100091" + Menge + "="
|
||
Local $sFormel = "100091" & $sMengeK & "="
|
||
; Auf 44 Zeichen auffüllen
|
||
While StringLen($sFormel) < 44
|
||
$sFormel &= " "
|
||
WEnd
|
||
$sFormel = StringLeft($sFormel, 44)
|
||
Return " " & $sFormel & $sOZCode & " "
|
||
EndFunc
|
||
|
||
; REB Blattadresse aus Zähler
|
||
; Format: Blattnummer(4) + Zeilenkennung(A-Z) + Index(0)
|
||
; idx=0 → "1000A0"
|
||
; idx=25 → "1000Z0"
|
||
; idx=26 → "1001A0" (nächste Blattnummer)
|
||
Func _REBOZCode($iIdx)
|
||
Local $iBlatt = 1000 + Int($iIdx / 26)
|
||
Local $iZeile = Mod($iIdx, 26)
|
||
Local $sZeile = Chr(65 + $iZeile) ; 65 = Asc("A")
|
||
Local $sBlatt = String($iBlatt)
|
||
Return $sBlatt & $sZeile & "0"
|
||
EndFunc
|
||
|
||
; Qty formatieren: ganzzahlig ohne Dezimalstellen, sonst 3 Stellen
|
||
; Komma als Dezimaltrenner (DA11-Standard)
|
||
Func _FmtQty($fVal)
|
||
Local $fAbs = $fVal
|
||
If $fAbs < 0 Then $fAbs = -$fAbs
|
||
Local $iGanz = Int($fAbs + 0.0005) ; gerundet
|
||
Local $fDez = $fAbs - Int($fAbs)
|
||
Local $iD3 = Int($fDez * 1000 + 0.5)
|
||
If $iD3 >= 1000 Then
|
||
$iGanz += 1
|
||
$iD3 = 0
|
||
EndIf
|
||
; Vorzeichenbehandlung
|
||
Local $sVorz = ""
|
||
If $fVal < 0 Then $sVorz = "-"
|
||
; Ganzzahlig? → kein Dezimalteil
|
||
If $iD3 = 0 Then
|
||
Return $sVorz & String($iGanz)
|
||
EndIf
|
||
; Mit Dezimalstellen: 3 Stellen, Komma
|
||
Local $sDez = String($iD3)
|
||
While StringLen($sDez) < 3
|
||
$sDez = "0" & $sDez
|
||
WEnd
|
||
; Trailing Zeros entfernen (z.B. 2,500 → 2,5 für saubere Darstellung)
|
||
While StringRight($sDez, 1) = "0" And StringLen($sDez) > 1
|
||
$sDez = StringLeft($sDez, StringLen($sDez) - 1)
|
||
WEnd
|
||
Return $sVorz & String(Int($fAbs)) & "," & $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, "&", "&")
|
||
$s = StringReplace($s, "<", "<")
|
||
$s = StringReplace($s, ">", ">")
|
||
$s = StringReplace($s, '"', """)
|
||
$s = StringReplace($s, "'", "'")
|
||
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
|