Загрузка данных


' ===== В САМОМ ВЕРХУ МОДУЛЯ (Personal.xlsb) =====
Private SavedRow As Long
Private SavedCol As Long
Private SavedNumLines As Long
Private SavedOldData As Variant
' =================================================

Sub ВставитьСписокИзWordСНумерацией()
    Dim dataObj As Object, clipText As String, linesArr As Variant
    Dim i As Long, lineText As String
    Dim numPart As String, textPart As String, extra1 As String, extra2 As String
    Dim dotPos As Long, numLines As Long
    Dim ws As Worksheet
    Dim rngBefore As Range

    ' 1. Читаем буфер обмена
    On Error Resume Next
    Set dataObj = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    If Err.Number <> 0 Then
        On Error GoTo 0
        Set dataObj = CreateObject("MSForms.DataObject")
    End If
    On Error GoTo 0

    If dataObj Is Nothing Then
        MsgBox "Не удалось получить доступ к буферу обмена.", vbExclamation
        Exit Sub
    End If

    dataObj.GetFromClipboard
    clipText = dataObj.GetText
    If Len(Trim(clipText)) = 0 Then
        MsgBox "Буфер обмена пуст или содержит не текст.", vbInformation
        Exit Sub
    End If

    ' 2. Разбиваем на строки, отбрасываем полностью пустые
    Dim rawLines As Variant
    rawLines = Split(clipText, vbCrLf)
    If UBound(rawLines) < 0 Then rawLines = Split(clipText, vbLf)

    Dim col As New Collection
    Dim rawLine As Variant
    For Each rawLine In rawLines
        If Len(Trim(CStr(rawLine))) > 0 Then
            col.Add CStr(rawLine)
        End If
    Next rawLine

    If col.Count = 0 Then
        MsgBox "Не найдено ни одной строки текста.", vbInformation
        Exit Sub
    End If

    ReDim linesArr(0 To col.Count - 1)
    For i = 0 To col.Count - 1
        linesArr(i) = col(i + 1)
    Next i
    numLines = col.Count

    ' 3. Сохраняем состояние ячеек (столбцы C, D, E, F)
    Set ws = ActiveSheet
    SavedRow = ActiveCell.Row
    SavedCol = ActiveCell.Column
    SavedNumLines = numLines

    Set rngBefore = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow + numLines - 1, SavedCol + 3))
    If Not rngBefore Is Nothing Then
        SavedOldData = rngBefore.Value
    Else
        SavedOldData = Empty
    End If

    ' 4. Вставляем строки и заполняем ВСЕ столбцы (C, D, E, F)
    Application.ScreenUpdating = False
    ws.Rows(SavedRow & ":" & SavedRow + numLines - 1).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove

    For i = 0 To numLines - 1
        lineText = linesArr(i)
        If Len(lineText) = 0 Then GoTo NextLine

        Dim parts As Variant
        parts = Split(lineText, vbTab)

        Dim v0 As Variant, v1 As Variant, v2 As Variant
        v0 = parts(0)
        If UBound(parts) >= 1 Then v1 = parts(1) Else v1 = Empty
        If UBound(parts) >= 2 Then v2 = parts(2) Else v2 = Empty

        Dim field1 As String, field2 As String, field3 As String
        field1 = SafeString(v0)
        field2 = SafeString(v1)
        field3 = SafeString(v2)

        If Len(field1) > 0 Then
            dotPos = InStr(field1, ". ")
            If dotPos > 0 And IsNumeric(Left(field1, dotPos - 1)) Then
                numPart = Left(field1, dotPos + 1)
                textPart = Trim(Mid(field1, dotPos + 2))
            Else
                numPart = ""
                textPart = field1
            End If
        Else
            numPart = ""
            textPart = ""
        End If

        With ws.Cells(SavedRow + i, SavedCol)        ' C
            .Value = numPart
            .Font.Bold = False
            .HorizontalAlignment = xlRight
        End With
        With ws.Cells(SavedRow + i, SavedCol + 1)    ' D
            .Value = textPart
            .Font.Bold = False
        End With
        With ws.Cells(SavedRow + i, SavedCol + 2)    ' E
            .Value = field2
            .Font.Bold = False
        End With
        With ws.Cells(SavedRow + i, SavedCol + 3)    ' F
            .Value = field3
            .Font.Bold = False
        End With
NextLine:
    Next i

    ' 5. Автоподбор высоты строк
    ws.Rows(SavedRow & ":" & SavedRow + numLines - 1).EntireRow.AutoFit

    ' 6. Удаляем последнюю строку, только если она полностью пустая И не содержит номер
    Dim lastRow As Long
    lastRow = SavedRow + numLines - 1

    If numLines > 1 Then
        If Not ws.Cells(lastRow, SavedCol).MergeCells Then
            If Not HasNumberInC(ws, lastRow, SavedCol) And IsRowReallyEmpty(ws, lastRow, SavedCol, SavedCol + 3) Then
                ws.Rows(lastRow).Delete Shift:=xlUp
                numLines = numLines - 1
                lastRow = lastRow - 1
            End If
        End If
    End If

    ' 7. Объединяем C и D в первой строке (если они ещё не объединены)
    If numLines > 0 Then
        Dim rngFirstCD As Range
        Set rngFirstCD = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow, SavedCol + 1))
        If rngFirstCD.MergeCells Then rngFirstCD.UnMerge
        Dim firstText As String
        firstText = SuperClean(CStr(ws.Cells(SavedRow, SavedCol + 1).Value))
        If Len(firstText) = 0 Then
            firstText = SuperClean(CStr(ws.Cells(SavedRow, SavedCol).Value))
        End If
        rngFirstCD.Merge
        rngFirstCD.Value = firstText
        rngFirstCD.Font.Bold = False
    End If

    ' 8. Умное объединение E и F (если все значения одинаковы)
    If numLines > 0 Then
        Dim rngE As Range, rngF As Range
        Set rngE = ws.Range(ws.Cells(SavedRow, SavedCol + 2), ws.Cells(lastRow, SavedCol + 2))
        Set rngF = ws.Range(ws.Cells(SavedRow, SavedCol + 3), ws.Cells(lastRow, SavedCol + 3))

        Dim dictE As Object, dictF As Object
        Set dictE = CreateObject("Scripting.Dictionary")
        Set dictF = CreateObject("Scripting.Dictionary")

        For i = 0 To numLines - 1
            Dim vE As Variant, vF As Variant
            vE = ws.Cells(SavedRow + i, SavedCol + 2).Value
            vF = ws.Cells(SavedRow + i, SavedCol + 3).Value
            If Not IsEmpty(vE) Then
                Dim normE As String
                normE = SuperClean(CStr(vE))
                If Len(normE) > 0 Then dictE(normE) = True
            End If
            If Not IsEmpty(vF) Then
                Dim normF As String
                normF = SuperClean(CStr(vF))
                If Len(normF) > 0 Then dictF(normF) = True
            End If
        Next i

        If dictE.Count = 1 Then
            If rngE.MergeCells Then rngE.UnMerge
            Dim uniqueE As String
            uniqueE = dictE.Keys()(0)
            rngE.ClearContents
            rngE(1).Value = uniqueE
            rngE(1).Font.Bold = False
            rngE.Merge
        End If

        If dictF.Count = 1 Then
            If rngF.MergeCells Then rngF.UnMerge
            Dim uniqueF As String
            uniqueF = dictF.Keys()(0)
            rngF.ClearContents
            rngF(1).Value = uniqueF
            rngF(1).Font.Bold = False
            rngF.Merge
        End If
    End If

    ' 9. Удаление строк до следующей объединённой ячейки в C или D
    Dim nextMergedRow As Long
    nextMergedRow = 0
    Dim maxRow As Long
    maxRow = ws.Rows.Count
    Dim r As Long
    For r = lastRow + 1 To WorksheetFunction.Min(lastRow + 1000, maxRow)
        If ws.Cells(r, 3).MergeCells Or ws.Cells(r, 4).MergeCells Then
            If ws.Cells(r, 3).MergeCells Then
                nextMergedRow = ws.Cells(r, 3).MergeArea.Row
            Else
                nextMergedRow = ws.Cells(r, 4).MergeArea.Row
            End If
            Exit For
        End If
    Next r

    If nextMergedRow > 0 Then
        Dim firstRowToDelete As Long, lastRowToDelete As Long
        firstRowToDelete = lastRow + 1
        lastRowToDelete = nextMergedRow - 1
        If lastRowToDelete >= firstRowToDelete Then
            ws.Rows(firstRowToDelete & ":" & lastRowToDelete).Delete Shift:=xlUp
        End If
    End If

    Application.ScreenUpdating = True
End Sub

' Проверка, что строка действительно пустая (во всех столбцах C-F нет ни одного НЕпробельного символа)
Private Function IsRowReallyEmpty(ws As Worksheet, rowNum As Long, colStart As Long, colEnd As Long) As Boolean
    Dim c As Long
    Dim regEx As Object
    Set regEx = CreateObject("VBScript.RegExp")
    regEx.Pattern = "\S"   ' любой непробельный символ
    regEx.Global = False

    For c = colStart To colEnd
        Dim cellValue As Variant
        cellValue = ws.Cells(rowNum, c).Value
        If Not IsEmpty(cellValue) Then
            If regEx.Test(CStr(cellValue)) Then
                IsRowReallyEmpty = False
                Exit Function
            End If
        End If
    Next c
    IsRowReallyEmpty = True
End Function

' Проверка, содержит ли ячейка C номер (цифры перед точкой и пробелом)
Private Function HasNumberInC(ws As Worksheet, rowNum As Long, colNum As Long) As Boolean
    Dim cellVal As Variant
    cellVal = ws.Cells(rowNum, colNum).Value
    If IsEmpty(cellVal) Then Exit Function
    Dim txt As String
    txt = Trim(CStr(cellVal))
    If Len(txt) = 0 Then Exit Function
    Dim pos As Long
    pos = InStr(txt, ". ")
    If pos > 1 Then
        Dim leftPart As String
        leftPart = Left(txt, pos - 1)
        If IsNumeric(leftPart) Then
            HasNumberInC = True
        End If
    End If
End Function

Private Function SuperClean(txt As String) As String
    If Len(txt) = 0 Then Exit Function
    Dim result As String
    result = txt
    result = Replace(result, Chr(160), " ")
    result = Replace(result, vbTab, " ")
    result = Replace(result, vbCrLf, " ")
    result = Replace(result, vbLf, " ")
    result = Replace(result, vbCr, " ")
    While InStr(result, "  ") > 0
        result = Replace(result, "  ", " ")
    Wend
    SuperClean = Trim(result)
End Function

Private Function SafeString(v As Variant) As String
    If IsNull(v) Or IsError(v) Then
        SafeString = ""
    Else
        SafeString = Trim(CStr(v))
    End If
End Function

Sub ОтменитьПоследнююВставкуСписка()
    If SavedNumLines <= 0 Then
        MsgBox "Нет данных для отмены последней вставки.", vbInformation
        Exit Sub
    End If

    Dim ws As Worksheet
    Set ws = ActiveSheet

    Application.ScreenUpdating = False

    ws.Rows(SavedRow & ":" & SavedRow + SavedNumLines - 1).Delete Shift:=xlUp

    If Not IsEmpty(SavedOldData) Then
        Dim rngRestore As Range
        Set rngRestore = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow + SavedNumLines - 1, SavedCol + 3))
        rngRestore.Value = SavedOldData
    End If

    SavedNumLines = 0
    SavedOldData = Empty

    Application.ScreenUpdating = True
End Sub