Excel VBA自动对帐

时间:2014-01-29 09:48:35

标签: excel vba excel-vba

我有一个excel工作表,如下所示,它列出了两种类型的交易 - Advance Pmt Credit&笔芯。目标是协调这一点,并确保每个提前Pmt信贷被一组补货抵消。

Date    Description  Amount     Grouping    AutoRef Sum Check   Ref Chk
12/7/2012   Refill   (20,000.00)    advpmtcrdt1     0   Ok
12/7/2012   Advance Pmt Credit   20,000.00  advpmtcrdt1 advpmtcrdt1 0   Ok
12/9/2012   Refill   (20,000.00)    advpmtcrdt2     0   Ok
12/9/2012   Refill   (40,000.00)    advpmtcrdt2     0   Ok
12/10/2012  Refill   (20,000.00)    advpmtcrdt2     0   Ok
12/10/2012  Advance Pmt Credit   80,000.00  advpmtcrdt2 advpmtcrdt2 0   Ok
12/11/2012  Refill   (40,000.00)    advpmtcrdt4     -100000 Ok
12/11/2012  Refill   (40,000.00)    advpmtcrdt4     -100000 Ok
12/11/2012  Refill   (20,000.00)    advpmtcrdt3     0   Ok
12/11/2012  Advance Pmt Credit   20,000.00  advpmtcrdt3 advpmtcrdt3 0   Ok
12/12/2012  Refill   (20,000.00)    advpmtcrdt4     -100000 Ok

实际数据从第1列到第3列;我使用第4列将每条记录分配给一个组;第5栏有公式为每个Advance Pmt Credit分配编号ref;第6栏和第6栏7进行了一些检查,以确保每个组添加到0,并且组中只存在1个Advance Pmt Credit。

我需要通过在第4列(分组)中添加引用来对行进行分组,以便

  1. 每组的总数应为0
  2. 每个组只能包含一个'Advance Pmt Credit'
  3. 规则2意味着第4栏中的值应与第5栏中的参考相同,如果它是提前Pmt学分。
  4. 我尝试编写一个宏来自动添加col 4中的引用:

    Sub AutoFill()
    
    Dim Ref As String
    Dim rng As Range
    
    
    Application.ScreenUpdating = False
    With Application.WorksheetFunction
    
    On Error Resume Next
    Ref = .VLookup("Unresolved", Range("FullList"), 3, 0)
    If Err.Number = "1004" Then Ref = .VLookup("Free", Range("List"), 2, 0)
    
    While .Index(Range("AB:AC"), .Match(Ref, Range("AB:AB"), 0), 1) <> Ok
    
        On Error Resume Next
        Ref = .VLookup("Unresolved", Range("FullList"), 3, 0)
        If Err.Number = "1004" Then Ref = .VLookup("Free", Range("List"), 2, 0)
    
        .Index(Range("N:O"), .Match(Ref, Range("O:O"), 0), 1) = Ref
    
        Set rng = Range("N1").End(xlDown).Offset(1, 0)
    
            While .SumIf(Range("N:N"), Ref, Range("L:L")) <> 0
            If .CountA(rng) = 0 Then
            rng.Value = Ref
            If .Sum(Range("P:P")) < 0 Then rng.Clear
            End If
            Set rng = rng.Offset(1, 0)
            Wend
    Wend
    
    End With
    
    Application.ScreenUpdating = True
    End Sub
    

    宏是这样做的:检查下一个未使用的ref,根据上面给出的规则3在col 4中分配它,然后开始为col 4中的第一个可用空白单元分配相同的ref,直到该组的总和是0;如果Refills的总和超过Advance Pmt信用额,则删除最后更新的ref;然后在col 3中找到下一个可用的空单元格并添加ref;然后再次检查总和...重复这些步骤,直到所有项目均衡。

    问题:宏运行平稳,直到它产生了大约15个组,但在第16次迭代中导致无限循环。如果在我们向组添加补充后补充的总和超过Advance Pmt信用总额,则此逻辑将失败。我上传了工作簿here

    你能否建议一个更好的算法来实现这个目标 - 无论是否有VBA。

    感谢您的帮助&amp;建议!

1 个答案:

答案 0 :(得分:0)

即使您对规则有所解释,我也发现很难破译您的代码所做的事情。但是,我可以将您的行分配匹配到组15,这是您的代码处理的最后一个。我的代码成功到35组,但在36组时优雅地失败。我将在后面解释原因。

我已经使用VBA在工作表中放置公式,但仅当这些工作表是动态的并且公式值将发生变化时。这似乎并非如此,所以我放弃了对公式的使用。

我像你一样使用N和O列。我也使用P列,但仅用于帮助您确定如何处理组36故障。我不使用你的任何其他工作栏。

与您一样,我在第一次传递时将参考号分配给“Advance Pmt Credit”行,并在第二次传递时将其他行分配给组。这不是必要的,但我相信保持两个通道分开使它们更清晰。

当我将行分配给组时,我使用列P记录运行总计。既然你已经给P列标题为“Sum Check”,那么也许你想要做同样的事情。 P列中与分配给组的最后一行的值始终为零。

我在第36组失败了,因为在达到运行总计为零之前,我用尽了行来分配给组。查看更新的工作表,我不清楚应该将哪些行分配给组36。

我认为最简单的方法是创建一份“Report.xlsb”。从副本中删除U到X列,并将宏AutoFill替换为我的。

试试我的宏。我对代码提供的帮助很少,因为我不想花时间解释可能不合适的解决方案。如果您认为它符合您的要求,我很乐意解释。

Option Explicit
Sub AutoFill()

  Const ColDesc As Long = 5
  Const ColAmount As Long = 12
  Const ColRefAll As Long = 14
  Const ColRefAPC As Long = 15
  Const ColTotal As Long = 16
  Const RefPrefix As String = "advpmtcrdt"

  Dim NumRefNext As Long
  Dim RefCrnt As String
  Dim Rng As Range
  Dim RowCrnt As Long
  Dim RowPrev As Long
  Dim TotalCrnt As Double

  With Worksheets("AS-Track")

    ' Allocate reference to each "Advance Pmt Credit" row
    ' ===================================================
    RowPrev = 1
    NumRefNext = 1
    ' Find first "Advance Pmt Credit" if any
    Set Rng = .Columns(ColDesc).Find(What:="Advance Pmt Credit", _
                                     After:=Cells(RowPrev, ColDesc), _
                                     SearchDirection:=xlNext)
    If Rng Is Nothing Then
      ' No "Advance Pmt Credit" in worksheet
      Call MsgBox("""Advance Pmt Credit"" not found", vbOKOnly)
      Exit Sub
    End If

    ' At least one "Advance Pmt Credit" row found.  On entry to this loop
    ' Rng addresses the first "Advance Pmt Credit" row.  For each subsequent
    ' loop, Rng addresses the followint "Advance Pmt Credit" row.
    Do While True
      If Rng.Row <= RowPrev Then
        ' Have looped so all "Advance Pmt Credit" rows have reference
        Exit Do
      End If
      RowCrnt = Rng.Row
      ' Allocate next reference to this "Advance Pmt Credit" row.
      .Cells(RowCrnt, ColRefAPC) = RefPrefix & NumRefNext
      NumRefNext = NumRefNext + 1
      RowPrev = RowCrnt
      Set Rng = .Columns(ColDesc).FindNext(Rng)
    Loop

    ' Allocate other rows to "Advance Pmt Credit" groups
    ' ==================================================

    ' This loop could have been merged with previous loop but I
    ' believe code is more easily understood if they are separate.

    ' Have exited above loop with Rng addressing first
    ' "Advance Pmt Credit" row.

    RowPrev = 1
    Do While True
      If Rng.Row <= RowPrev Then
        ' Have looped so "Advance Pmt Credit" rows have been grouped
        ' with other rows.
        Exit Do
      End If
      RowCrnt = Rng.Row

      ' This is a "Advance Pmt Credit" row.  Group it with previous
      ' non-"Advance Pmt Credit" rows that are not part of another
      ' group until the group total is zero.

      RefCrnt = .Cells(RowCrnt, ColRefAPC).Value
      .Cells(RowCrnt, ColRefAll).Value = RefCrnt
      TotalCrnt = .Cells(RowCrnt, ColAmount).Value
      With .Cells(RowCrnt, ColTotal)
        .NumberFormat = "#,##0.00-"
        .Value = TotalCrnt
      End With

      Do While True
        RowCrnt = RowCrnt - 1
        If RowCrnt = 1 Then
          Call MsgBox("I am trying to create the group " & RefCrnt & _
                      " but I reached row 1 with a running total of " & _
                      Format(TotalCrnt, "#,##0.00-"), vbOKOnly)
          Exit Sub
        End If
        If .Cells(RowCrnt, ColRefAll).Value = "" Then
          ' This row has not been allocated
          .Cells(RowCrnt, ColRefAll).Value = RefCrnt
          TotalCrnt = TotalCrnt + .Cells(RowCrnt, ColAmount).Value
          With .Cells(RowCrnt, ColTotal)
            .NumberFormat = "#,##0.00-"
            .Value = TotalCrnt
          End With

          If Abs(TotalCrnt) < 0.009 Then
            ' The total of this group is zero (within the error associated
            ' with holding decimal values as binary).
            Exit Do
          End If
        End If
      Loop

      RowPrev = Rng.Row
      Set Rng = .Columns(ColDesc).FindNext(Rng)
    Loop

  End With

End Sub