如何分组循环数据表

问题描述 投票:0回答:1

我想在 Excel VBA 宏中检查标准化表格中的单元格(意味着修复列标题,但没有修复位置,以便表格可以在修复命名工作表中的任何位置开始)。

以下是该表的示例: Example table

示例表说明: 该数据用于开票。在名为“发票 ID”的 S 列中,有发票的 ID,这意味着第 72 行(ID=1)是一张发票,有一个发票位置,因此总共只有一笔金额(此处为 5 欧元)。第二张发票(ID=2)包含两个发票仓位,总金额为10港币(包括8+2港币仓位)。

使用 VBA 宏,我想检查表中每个单元格的条目和格式是否正确。另外,对于多位置发票的情况,我想检查总金额是否是各个位置的正确总和(在发票2的示例中:我想检查总金额10是否是正确的总和8 和 2)。 总金额始终放在发票组的第一行。

我已经创建了以下代码,但它不起作用。

有人知道问题是什么或代码如何工作吗?

非常感谢您的帮助!

史蒂菲

Option Explicit
Private WB As Workbook, ws As Worksheet
Private i As Long, lEnde As Long, strHeader As String
Private rngFind As Range, booCheck As Boolean, rngHeader As Range, rngFormula As Range, rngKey As Range, rngUsed As Range
Private idCol As Range
Private headerRow As Range
Private dataRange As Range
Private currentID As Variant
Private previousID As Variant
Private groupStartRow As Long
Private groupEndRow As Long
Private lastRow As Long
Private lastCol As Long
Private numRows As Long 
Private isMultiLine As Boolean
Private cell As Range
Private col As Range
Private groupRange As Range
Private groupRow As Range
Private rowIndex As Long
Private cellRef As Range
Private cellValue As Variant
Private cellFormula As String
Private cellFormat As String
Private containsLineBreak As Boolean
 
Function Main_Check(ByVal strFilePath As String) As String
On Error GoTo ErrorHandler
If strFilePath = "" Then GoTo ErrorHandler
Set WB = Workbooks.Open(strFilePath)
Set ws = WB.Worksheets("SpecificSheet")
With ws
 '//Define last row and column which must be processed
lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row
lColEnde = .UsedRange.SpecialCells(xlCellTypeLastCell).Column
'//Find beginning of table
Set rngFind = .Cells.Find(what:=Settings.Cells(Settings.Range("Header_Start").Row + 1, 2).Value, LookIn:=xlValues, lookat:=xlWhole)
If rngFind Is Nothing Then
    booCheck = False
    End
End If
 
lEnde = .Cells(.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 2, 2).End(xlUp).Row

Set rngUsed = .Range(rngFind.Address, .Cells(lEnde, lColEnde))
booCheck = IsErrorAll(rngUsed)
 
'//Define header row und ID-column
Set headerRow = rngUsed.EntireRow
lastRow = .Cells(.Rows.Count, rngUsed.Column).End(xlUp).Row
lastCol = .Cells(headerRow.Row, .Columns.Count).End(xlToLeft).Column
Set idCol = .Range(.Cells(headerRow.Row + 1, rngUsed.Column), .Cells(lastRow, lastCol))

'//Group for ID
currentID = idCol.Cells(1, 1).Value

    'Check if first group has ID = 1
    If currentID <> 1 Then
        currentID.Interior.Color = vbRed
        booCheck = False
    End If
    If booCheck = False Then GoTo Ende

groupStartRow = idCol.Cells(1, 1).Row
previousID = currentID 'Initialize first group

For i = 2 To idCol.Rows.Count + 1 'Loop via IDs
    If i > idCol.Rows.Count Or idCol.Cells(i, 1).Value <> currentID Then
        'Group end reached
        groupEndRow = idCol.Cells(i - 1, 1).Row
    
        'Process group
        Call ProcessGroup(.Rows(groupStartRow & ":" & groupEndRow), .Rows(headerRow))
    
        'Check ongoing IDs
        If i <= idCol.Rows.Count Then
            Dim nextID As Variant
            nextID = idCol.Cells(i, 1).Value
            If nextID <> previousID + 1 Then
                idCol.Cells(i, 1).Interior.Color = vbRed
                booCheck = False
            End If
            If booCheck = False Then GoTo Ende
            previousID = nextID 'Set the new last ID
        End If
        
        'Start new group
        If i <= idCol.Rows.Count Then
            currentID = idCol.Cells(i, 1).Value
            groupStartRow = idCol.Cells(i, 1).Row
        End If
    End If
Next i
End Function

Sub ProcessGroup(groupRange As Range, headerRow As Range)
'Check if group is multi-line
isMultiLine = (groupRange.Rows.Count > 1)
 
'Check all columns and all rows of group
rowIndex = 1 'Initialize row index within group
If groupRange.Rows.Count = 1 And groupRange.Columns.Count = 1 Then
   Set cellRef = groupRange
Else
   Set cellRef = groupRange.Cells(1, 1)
End If
 
For Each groupRow In groupRange.Rows
Call Processing1(groupRow)
rowIndex = rowIndex + 1 'Increase row index
Next groupRow
End Sub
 
Sub Processing1(groupRow As Range)
   '//Invoice-ID (only formal check, content already checked)
    strKey = "ID"
    Set rngKey = Settings.Cells(1, 1).EntireColumn.Find(what:=strKey, LookIn:=xlValues, lookat:=xlWhole)
    strHeader = Settings.Cells(rngKey.Row, 2).Value
    Set rngHeader = ws.Range(rngFind, ws.Cells(rngFind.Row, lColEnde)).Find(what:=strHeader, LookIn:=xlValues, lookat:=xlWhole)
    cellRef = ws.Cells(groupRow.Row, rngHeader.Column) 
    containsLineBreak = (InStr(1, cellRef.Value, vbLf) > 0)
    
    If (cellRef.Value Like "#" Or cellRef.Value Like "##" Or cellRef.Value Like "###") 
    And cellRef.NumberFormat = "General" And Not containsLineBreak And Not Left(cellRef.Formula, 2) = "=+" Then
        cellRef.Interior.Pattern = xlNone
    Else
        cellRef.Interior.Color = vbRed
        booCheck = False
    End If
    
    '//.... (further checks for further columns)
End Sub
excel vba datatable
1个回答
0
投票

我为你编写了一个代码,通过 S 列找到 ID 并验证总数。只有一个位置的 ID 在此代码中将被标记为不正确。

请更改相关名称和列,例如(工作表名称为发票),说明将位于 AA 列(i,29)。

    Sub CheckInvoices()
        
        
        Dim ws As Worksheet
        Set ws = ThisWorkbook.Sheets("Invoices") ' Change to your actual sheet name
    
        Dim lastRow As Long
        lastRow = ws.Cells(ws.Rows.Count, "S").End(xlUp).Row ' Find the last row with data in column S
    
    
        Dim i As Long
        Dim currentInvoiceID As Long
        Dim sumPositions As Double
        Dim totalAmount As Double
        Dim isTotalAmountRow As Boolean
        Dim invoiceStartRow As Long
    
        On Error GoTo ErrorHandler
    
        For i = 2 To lastRow ' Assuming row 1 is headers
            If IsNumeric(ws.Cells(i, 19).Value) Then ' Check if cell in column S contains a numeric value (Invoice ID)
                
             If currentInvoiceID <> ws.Cells(i, 19).Value Then ' New invoice found
                    
              If currentInvoiceID <> 0 Then ' Skip the first iteration
                        ' Check previous invoice's total amount and write validation message
                If sumPositions = totalAmount Then
                            ws.Cells(invoiceStartRow, 27).Value = "Sum of Positions " & currentInvoiceID & " are OK"
                        
                  Else
                            ws.Cells(invoiceStartRow, 27).Value = "Sum of Positions " & currentInvoiceID & " is not correct"
                        
                End If
            
               End If
                    ' Reset for the new invoice
                    currentInvoiceID = ws.Cells(i, 19).Value
                    sumPositions = 0
                    isTotalAmountRow = True
                    invoiceStartRow = i ' Track the starting row of the invoice
                
              End If
                
                ' Check if it's the total amount row
                If isTotalAmountRow Then
                    totalAmount = ws.Cells(i, 24).Value ' Assuming total amount is in column X
                    isTotalAmountRow = False
                
               Else
                    ' Sum the positions
                    sumPositions = sumPositions + ws.Cells(i, 24).Value ' Assuming position amounts are in column X
                
             End If
            
            End If
        
        Next i
        
        ' Check last invoice's total amount and write validation message
        If currentInvoiceID <> 0 Then ' Ensure it's not the first iteration
            
              If sumPositions = totalAmount Then
                ws.Cells(invoiceStartRow, 27).Value = "Sum of Positions " & currentInvoiceID & " are OK"
            
                 Else
                ws.Cells(invoiceStartRow, 27).Value = "Sum of Positions " & currentInvoiceID & " is not correct"
            
                   End If
        
               End If
    
        Exit Sub
    
    ErrorHandler:
    
        MsgBox "Error " & Err.Number & ": " & Err.Description & " at row " & i & " column " & invoiceStartRow
    
    
    End Sub
© www.soinside.com 2019 - 2024. All rights reserved.