Excel·VBA自动生成日记账的对方科目

文章介绍了一种使用VBA在Excel中处理日记账科目匹配的方法,包括一对一和一对多的情况。代码首先遍历数组,通过判断借贷方向生成相反科目,对于多对多的情况,由于可能涉及组合求和,处理起来更为复杂,因此单独处理。在一对多的情况下,代码能有效地进行匹配,而在多对多时,由于可能的数据组合数量大,处理时间会增加,建议优先处理数据量小的部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
如图:根据日记账/序时账的日期、凭证号为一组,按借贷方向生成相反的科目,并写入H列。可能存在一对一、一对多、多对多等情况的账目

数组法遍历、判断、写入

适用日期凭证号连续的日记账

按照判断难易程度从简单开始,先判断科目一对一的同向/反向情况;再判断科目一对多且借方和贷方数组剩余数据刚好相等的情况;最后再判断多对多的情况,由于多对多可能涉及组合求和问题,耗时会比较长,因此以下代码删除了多对多的情况,另写一个sub专门处理多对多问题。(数据匹配后,对应的数组该数据会清空,方便后续判断)

Sub 生成对方科目_一对多()
    '适用日期凭证号连续的日记账,一对多版;start_end(1)限制代码运行结束行数
    Dim arr, d, e, f, res, i&, j&, x&, m, brr, b, write_col$, s$, ss$, s1$, s2$
    tm = Timer: write_col = "h"    '结果写入列号
    start_end = Array(2, 0)  '开始结束行号
    With ActiveSheet
        arr = .[a1].CurrentRegion
        If start_end(1) = 0 Then start_end(1) = UBound(arr)  '结束行号默认最后一行
        Do
            ReDim d(1 To 100): ReDim e(1 To 100): ReDim f(1 To 100)
            s = arr(start_end(0), 1) & arr(start_end(0), 2): x = 0
            For i = start_end(0) To UBound(arr)
                ss = arr(i, 1) & arr(i, 2)
                If s = ss Then x = x + 1: d(x) = arr(i, 4): e(x) = arr(i, 5): f(x) = arr(i, 6)
                If s <> ss Or i = UBound(arr) Then
                    ReDim Preserve d(1 To x): ReDim Preserve e(1 To x): ReDim Preserve f(1 To x)
                    ReDim res(1 To x): Exit For
                End If
            Next
            '金额判断科目
            For t = 1 To 2  '执行2次循环,尽可能多配对
                len_e = Len(Join(e, "")): len_f = Len(Join(f, ""))
                If len_e Or len_f Then    '不为空数组
                    For i = 1 To x    '一对一,一对多
                        If Len(e(i)) Then    '一借一贷
                            m = Application.Match(e(i), f, 0)
                            If TypeName(m) <> "Error" Then res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): e(i) = "": f(m) = ""
                        End If
                        If Len(f(i)) Then    '一借一贷
                            m = Application.Match(f(i), e, 0)
                            If TypeName(m) <> "Error" Then res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): f(i) = "": e(m) = ""
                        End If
                        If Len(e(i)) Then    '借方一正一负
                            m = Application.Match(-e(i), e, 0)
                            If TypeName(m) <> "Error" Then res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): e(i) = "": e(m) = ""
                        End If
                        If Len(f(i)) Then    '贷方一正一负
                            m = Application.Match(-f(i), f, 0)
                            If TypeName(m) <> "Error" Then res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): f(i) = "": f(m) = ""
                        End If
                        If Len(e(i)) Then    '一借多贷,剩余金额相等;计算精度问题
                            ts = WorksheetFunction.sum(f)
                            If e(i) = ts Or Abs(Round(e(i) - ts, 6)) < (0.1 ^ 6) Then
                                For j = 1 To x
                                    If Len(f(j)) Then res(i) = res(i) & "," & d(j): res(j) = res(j) & "," & d(i): e(i) = "": f(j) = ""
                                Next
                            End If
                        End If
                        If Len(f(i)) Then    '多借一贷,剩余金额相等
                            ts = WorksheetFunction.sum(e)
                            If f(i) = ts Or Abs(Round(f(i) - ts, 6)) < (0.1 ^ 6) Then
                                For j = 1 To x
                                    If Len(e(j)) Then res(i) = res(i) & "," & d(j): res(j) = res(j) & "," & d(i): f(i) = "": e(j) = ""
                                Next
                            End If
                        End If
                    Next
                    If len_e = 0 And Abs(WorksheetFunction.sum(f)) < (0.1 ^ 6) Then
                        s1 = "": s2 = ""  '借方为空,贷方和为0,多正多负
                        For tt = 1 To 2   '第1遍读取数据,第2遍写入数据
                            For i = 1 To x
                                If Len(f(i)) And f(i) > 0 Then
                                    If tt = 1 Then s1 = s1 & "," & d(i)
                                    If tt = 2 Then res(i) = s2: f(i) = ""
                                ElseIf Len(f(i)) And f(i) < 0 Then
                                    If tt = 1 Then s2 = s2 & "," & d(i)
                                    If tt = 2 Then res(i) = s1: f(i) = ""
                                End If
                            Next
                        Next
                    ElseIf len_f = 0 And Abs(WorksheetFunction.sum(e)) < (0.1 ^ 6) Then
                        s1 = "": s2 = ""  '贷方为空,借方和为0,多正多负
                        For tt = 1 To 2   '第1遍读取数据,第2遍写入数据
                            For i = 1 To x
                                If Len(e(i)) And e(i) > 0 Then
                                    If tt = 1 Then s1 = s1 & "," & d(i)
                                    If tt = 2 Then res(i) = s2: e(i) = ""
                                ElseIf Len(e(i)) And e(i) < 0 Then
                                    If tt = 1 Then s2 = s2 & "," & d(i)
                                    If tt = 2 Then res(i) = s1: e(i) = ""
                                End If
                            Next
                        Next
                    End If
                End If
            Next
            For i = 1 To x    '清除开头的","
                If Len(res(i)) Then res(i) = Mid(res(i), 2)
            Next
            .Cells(start_end(0), write_col).Resize(x, 1) = WorksheetFunction.Transpose(res)
            start_end(0) = start_end(0) + x
        Loop Until start_end(0) > UBound(arr) Or start_end(0) > start_end(1)
    End With
    Debug.Print "用时:" & Format(Timer - tm, "0.00")  '耗时
End Sub
测试结果

在15248行日记账中,生成了12858行的对方科目,用时0.59秒
可以处理科目一对一、一对多的情况,以及同方向的多对多和为0的情况(如图)
在这里插入图片描述

多对多问题处理

考虑到多对多问题,涉及组合求和问题,耗时会比较长,因此 start_end(1) 参数控制代码运行行数。且不修改一对多版生成结果
组合求和问题调用了combin_arr1函数,代码详见《Excel·VBA数组组合函数、组合求和》(如需使用代码需复制)

Sub 生成对方科目_多对多()
    '适用日期凭证号连续的日记账,多对多版;start_end(1)限制代码运行结束行数;不修改一对多版生成结果
    Dim arr, d, e, f, res, i&, j&, x&, m, brr, b, write_col$
    tm = Timer: write_col = "h"  '结果写入列号
    start_end = Array(2, 0)  '开始结束行号
    With ActiveSheet
        arr = .[a1].CurrentRegion
        If start_end(1) = 0 Then start_end(1) = UBound(arr)  '结束行号默认最后一行
        hrr = .Cells(1, write_col).Resize(UBound(arr), 1)   'h列数据
        Do
            For i = start_end(0) To UBound(arr)  'h列为空
                If Len(hrr(i, 1)) = 0 Then start_end(0) = i: Exit For
            Next
            ReDim d(1 To 100): ReDim e(1 To 100): ReDim f(1 To 100): ReDim res(1 To 100)
            s = arr(start_end(0), 1) & arr(start_end(0), 2): x = 0
            For i = start_end(0) To UBound(arr)
                ss = arr(i, 1) & arr(i, 2)
                If s = ss Then
                    x = x + 1
                    If Len(hrr(i, 1)) = 0 Then  'h列为空
                        d(x) = arr(i, 4): e(x) = arr(i, 5): f(x) = arr(i, 6)
                    Else
                        res(x) = "," & hrr(i, 1)  '不修改原版生成结果
                    End If
                End If
                If s <> ss Or i = UBound(arr) Then
                    ReDim Preserve d(1 To x): ReDim Preserve e(1 To x): ReDim Preserve f(1 To x)
                    ReDim Preserve res(1 To x): Exit For
                End If
            Next
            '金额判断科目
            For i = 1 To x  '一借一贷,一对多
                If x > 20 Then Debug.Print "数据太多,求和速度慢": Exit For
                If Len(e(i)) Then    '一借一贷,一对多
                    For j = x - 1 To 2 Step -1
                        brr = combin_arr1(f, j)  '调用函数返回组合,一维嵌套数组
                        For Each b In brr
                            temp_sum = WorksheetFunction.sum(b)
                            If temp_sum = e(i) Then
                                For Each bb In b
                                    If Len(bb) Then
                                        m = Application.Match(bb, f, 0)
                                        res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): f(m) = ""
                                    End If
                                Next
                                e(i) = "": Exit For
                            End If
                        Next
                        If e(i) = "" Then Exit For
                    Next
                End If
                If Len(f(i)) Then    '一借一贷,一对多
                    For j = x - 1 To 2 Step -1
                        brr = combin_arr1(e, j)
                        For Each b In brr
                            temp_sum = WorksheetFunction.sum(b)
                            If temp_sum = f(i) Then
                                For Each bb In b
                                    If Len(bb) Then
                                        m = Application.Match(bb, e, 0)
                                        res(i) = res(i) & "," & d(m): res(m) = res(m) & "," & d(i): e(m) = ""
                                    End If
                                Next
                                f(i) = "": Exit For
                            End If
                        Next
                        If f(i) = "" Then Exit For
                    Next
                End If
            Next
            If Len(Join(e, ",")) >= x Or Len(Join(f, ",")) >= x Then
                For i = 1 To x    '多借多贷,无法组合求和
                    If Len(e(i)) Then
                        For j = 1 To x
                            If Len(f(j)) Then res(i) = res(i) & "," & d(j): res(j) = res(j) & "," & d(i)
                        Next
                    End If
                Next
            End If
            For i = 1 To x    '清除开头的","
                If Len(res(i)) Then res(i) = Mid(res(i), 2)
            Next
            .Cells(start_end(0), write_col).Resize(x, 1) = WorksheetFunction.Transpose(res)
            start_end(0) = start_end(0) + x
        Loop Until start_end(0) > UBound(arr) Or start_end(0) > start_end(1)
    End With
    Debug.Print "用时:" & Format(Timer - tm, "0.00")  '耗时
End Sub
测试结果

由于耗时较长,仅部分测试

多对多组合问题
在这里插入图片描述

多对多非组合问题
在这里插入图片描述
存在问题
在这里插入图片描述
从特殊情况可知,多对多问题一方数据量较大时,耗时增长明显;而数据量在10以内时,即便需要组合求和耗时也很少,因此编写代码时可以考虑优先处理数据量较小的部分,跳过数据量较多的部分

扩展阅读
《excelhome-如何通过VBA自动生成对方科目》

税审大师V6升级 1.支持2010中税协新税审指导南 (1)标准税审模板 ①重新设置了标准税审模板 ②对中税协底稿进行了全面分析,部分底稿重新按新指南设计,对其部分不足的底稿仍保留原底稿(比如:中税协部分底稿是一种通用格式,没有根据会计科目核算内容设计。) ③中税协指南中不含资产负债表类底稿,我们保留了这些底稿供选用,以适就不同地方的税审要求,底稿中的用词按中税协底稿来重新设计(比如将原来的审核(审核数,审核调整数)修改为鉴证(鉴证数,鉴证调整数)),以保持体系统的一致性。 ④底稿索引设计成两个表,一个是中税协的底稿索引,另一个是资产负债表底稿索引(如果不要求做资产负债表底稿的地方,就不需要打印这个索引表),增加归档的灵活性。 (2)对其他审计模板,如江苏模板,仍保留原有特色,以待各税协的意见。 2.取数工具升级 (1)增加了用友金蝶账套备份取数,对用友账套备份自动解压 (2)增加了用友U盘版(记账宝),金蝶U盘版(记账王)取数,所有用友金蝶取数时,增加了对凭证的判断,防止取回去才发现是空账套 (3)增加了航天信息A6/U6取数 (4)增加了常隆天健财务软件 (5)增加了四方自动取数(含辅助账) (6)增加了金算盘6f专业版和金算盘eERP-B (7)增加了小密蜂/密蜂源V6/V7的取数 (8)增加了金财V6多账套的智能识别,对金财2008增加了自动取数(含辅助账) (9)增加了青岛高信G-EA标准接口数据导入功能 (10)浪涛备份取数工具增加了对有些浪涛版本用负数表示贷方余额的情况 (11)取数工具主界面分成两个区,左边显示财务软件公司,右边显示版本,操作更简易 (12)增加了Sql server账套备份工具,对没有的财务软件账套可以备份出来 (13)取数工具提供一个轻便版本(用友金蝶版),便于发给客户取数 (14)增加了对商友财务软件数据接口读取功能。 3.增加了对方科目分析功能,可以按借方或贷方分析,可以分月或按年分析 4.Excel数据导入增强 (1)增加了对科目编码同科目名称在同一列且科目编码与科目名称之间没有空格的识别,并智能分离科目编码与名称(比如有些速达软件导出的科目是[1002银行存款]) (2)对科目编码不可见字符识别(有些财务软件,比如个别版本的用友软件,导出的科目编码后加了一个换行符,看上去科目编码是“1002”,实际上是5个字符,后面多了一个不可见的换行符。),增加了自动删除科目编码中不可见字符功能 5.税审报告及税审说明重大升级 (1)税审报告及税审说明全部改成Word版,并自动生成税审报告及税审说明(自动链接数据源中的数据),格式和表格都已完美设计,克服了Excel附注不好排版和增删表格困难的问题,极大提高了效率 (2)Excel报表和数据源中链接数据速度提高6倍 6.改进了凭证抽查 (1)修正了按摘要排除不准确的问题 (2)修正了已抽凭证有时没有保存的问题 (3)提高了生成抽凭表的速度 (4)对行数多的凭证汇总功能进行了改进(汇总凭证抽查表上显示“/...”;对借方和贷方都有发生的按大的一方对冲小的一方列示到抽凭表上.) 7.会计科目同税法科目对应功能 对新指南的主营业务收入/主营业务成科目,将会计科目(明细)按税法科目进归类分析并生成底稿,将税审软件功能从生成底稿向未来的按税法科目的方向发展。 8.数据导入功能改进 (1)对明细科目同总账科目方向相反处理为负数(如应收账款贷方余额明细) (2)增加了未记账凭证的识别,并自动汇总发生额
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔_51

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值