百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Excel VBA 应用案例分享/电子发票管理小助手/电子发票信息读取

yuyutoo 2025-02-04 16:54 3 浏览 0 评论

本文于2023年5月31日首发于本人同名公众号:Excel活学活用,更多文章案例请搜索关注!

☆本期内容概要☆

  • 发票文件选择,取得文件名
  • VBA正则表达式
  • PDF文件信息读取
  • OFD文件信息读取

大家好,我是冷水泡茶,这几天一直在修改我的《电子发票管理系统》,这是我一年多以前做的一个应用,在公司里正常使用。搞这个系统的缘由很简单,发票数字化、电子化是一个不可阻挡的趋势,看看最近多家省市又开始全电发票试点的新闻就知道了。数字发票好处是很多,但是对我们财务人员来讲,却谈不上有多少好处。困难倒是不少,主要有两个,一是电子发票可以重复打印,如何避免重复报销入账(不管是有意还是无意),都是一个难题;二是电子数据归档问题,财政部和国家档案局都有明确规定,简要说来就是,各个会计主体、核算单位,都要把电子发票的数据文件保存下来,如果有ERP、OA等信息系统的,可以只保存电子文档,其他以打印出来的纸质发票报销入账的单位,要同时保存相应的电子发票文档,并且要求与会计凭证能够对应,方便查询,至少要把每个月的电子发票归类到一个独立的目录中,比如202305之类的。

虽然很多单位都没能做到这一点,好象也没有谁来查,但我们不能就这样糊弄着过吧,总得做点什么。

我看到有的财务是自己登记的,把发票号码输入到一个EXCEL表中,可以检查是否重复,但不仅工作量大,而且容易出错,万一号码登记错了呢?增加了重复报销的可能性,那不都变成财务的责任了?这样的事,咱是不干的;还有发票电子文档的收集、整理,那也是让人崩溃的事,所有这些,最后都集中到财务这来,真是压力山大呀。这咱也不能干。

事情还是要做的,那就寻找解决办法呗。

经过一段时间(具体多少日子记不清了),我终于完成了《电子发票管理系统》的设计,以EXCEL为客户端,Access为数据存储端,以共享文件夹为发票电子文档、Access数据库保存目录,实现每人可以在自己的电脑登录操作。基本流程是这样的:

1、报销人下载、打印好电子发票,然后登录系统,进入“发票登记”模块,填写发票的关键信息,选择电子发票文件完整路径。

2、填写完毕后保存,程序会检验发票号码是否重复,重复则不能保存。各种校验无误后,自动将电子发票的文档复制到共享的数据库文件夹里,同时可以自动打印“报销单”,这个功能是省事了。

3、报销人持报销单及发票打印件到财务审核,财务人员登录系统,进入“发票审核”模块,双击一条记录,可以打开发票电子文档,进行核对,核对无误后点审核。

4、财务审核后,报销人将报销单报领导审批(线下签字),审批完成后,交财务出纳处付款。出纳登录系统,进入“出纳付款”模块,列出已审核未付款的报销单,确认无误后点付款。

5、最后,财务记账归档,财务人员登录系统,进入“发票记账“模块,列出所有已付款未记账的报销单,对应到财务记账凭证号,把凭证号录入系统,这样可以方便地查询。整个流程结束。

看起来还是不错的,就这样运行了一年多,也没出什么问题。我是财务审核人,不忙的时候就看得仔细点,忙的时候就看得马虎些,自始至终没有审出一张有问题的发票来。但心里总是隐隐感觉有那么点不踏实。

本来也没有想去更新这个系统,直到我自己填了几次报销单,就一个字:烦!虽然我也搞了一些复制发票、项目选择的功能,以减轻录入发票的工作量,但是真的比较烦,工作量确实比较大。于是萌生了一个念头:要是能自动读取发票信息就好了!

于是就上网各种搜,“电子发票信息读取”、“EXCEL操作PDF”......

最后,搜到一个公众号文章:【电子发票台账T006(V1.1.3)】,他提供了文件下载,于是赶紧下载来看看。他的功能是是读取一个文件夹下所有发票文件,然后把关键信息登记下来,形成一个发票台账。

他的代码对我来说非常有价值,在此表示感谢。他的思路简要说明如下:

1、PDF文档可以转换成WORD文档,然后读取WORD文档的内容,运用正则表达式提取发票关键信息,要求电脑上装有“Adobe Acrobat Pro“。

2、OFD文档可以通过RAR解压,得到XML文档,然后读取XML文档内容,提取发票关键信息,要求电脑上装有RAR压缩软件。

扯了这么多,终于到我今天要分享的工具了“电子发票管理助手”。在“电子发票台账T006”在基础上,按照我自己需求,进行了重新编写:

1、参考的地方:

(A)总体思路,发票文件的读取、转化方法。

(B)部分正则表达式。

(C)其他

2、优化与改进的地方:

(A)RAR程序是否安装的判断与路径的直接获取。

(B)临时文件夹建立、删除方式。

(C)OFD文件的信息提取方式,并增加了最新发票格式的读取。

(D)正则表达式优化与扩展,增加了2种版本的发票读取。

(E)获取的发票信息采取变量方式存储。

下面来说一说我的“电子发票管理助手”设计过程与思路吧

1、定义一个过程 ReadInvoiceFile

Sub ReadInvoiceFile()
    On Error Resume Next
    Dim FileExtn As String
    Dim iRow As Integer
    InvoiceCode = ""
    InvoiceNo = ""
    SellerName = ""
    SellerTaxID = ""
    Amount = ""
    TaxAmount = ""
    invoiceDate = ""
    ItemName = ""
    BuyerName = ""
    currInvoiceFile = FileSelected
    If currInvoiceFile = "" Then Exit Sub


    FileExtn = GetExtn(currInvoiceFile)
    'Stop
    If FileExtn = ".pdf" Then
        Call ReadPDFInvoiceInfo
    ElseIf FileExtn = ".ofd" Then
        Call ReadOFDInvoiceInfo
    End If
    '发票信息写入工作表
    Sheets("Result").Activate
    With ActiveSheet
        iRow = .UsedRange.Rows.Count + 1
        Cells(iRow, 1) = BuyerName
        Cells(iRow, 2) = invoiceDate
        Cells(iRow, 3) = "'" & InvoiceCode
        Cells(iRow, 4) = "'" & InvoiceNo
        Cells(iRow, 5) = SellerName
        Cells(iRow, 6) = "'" & SellerTaxID
        Cells(iRow, 7) = ItemName
        Cells(iRow, 8) = Amount
        Cells(iRow, 9) = TaxAmount
        Cells(iRow, 10) = CDbl(Amount) + CDbl(TaxAmount)
        .Hyperlinks.Add Anchor:=.Cells(iRow, 11), _
        Address:=currInvoiceFile, _
        TextToDisplay:="打开文件"
    End With
    MsgBox "发票信息读取完毕,请仔细核对! " & Chr(10) & "若有错误,请手工修改!" & Chr(10) & "空白部分,请手工填写!"
End?Sub

代码解析:

先把一些公共变量值清空,然后选择文件,根据文件后缀判断,是PDF的,我们运行ReadPDFInvoiceInfo过程,是OFD的,我们运行ReadOFDInvoiceInfo过程,这两个过程都是用来读取发票信息的,读取到的信息存到变量中,完成后,回到本过程再把发票信息写入工作表,并添加发票文件链接。

2、读取PDF文件过程ReadPDFInvoiceInfo,代码较多,我贴到第二条文章。

(1)设置临时文件夹c:\temp\,如果没有则创建。

tempFolder = "c:\temp\"
If?Dir(tempFolder,?vbDirectory)?=?""?Then
????MkDir?tempFolder
End?If

(2)创建 Acrobat 应用程序对象,打开PDF发票文件,转存为WORD文件。

'创建?Acrobat?应用程序对象
Set acrobatApp = CreateObject("AcroExch.App")
'创建 Acrobat AVDoc 对象
Set AcrobatAVDoc = CreateObject("AcroExch.AVDoc")
'打开选择的 PDF 文件
AcrobatAVDoc.Open currInvoiceFile, ""
acrobatApp.Hide
Set AcrobatPDDoc = AcrobatAVDoc.GetPDDoc
Set jsObj = AcrobatPDDoc.getjsobject
'创建 Word 应用程序对象
Set WordApp = CreateObject("Word.Application")
'关闭安全提示
WordApp.Application.AutomationSecurity = msoAutomationSecurityForceDisable
'将 PDF 转换为 Word
WordFilePath = tempFolder & Format(Time, "hhmmss") & ".docx"
jsObj.SaveAs WordFilePath, "com.adobe.acrobat.docx"
'关闭和清理 Acrobat
AcrobatAVDoc.Close 1
acrobatApp.Exit

(3)打开WORD文件,读取信息到变量wordContent,然后通过正则表达式提取相应的发票关键字信息存入变量。

(4)读取完成后,删除临时文件夹。这里建立了一个删除文件夹的过程,看看代码就知道是ChatGPT生成的,基本没改。

Sub DeleteDirectory(ByVal folderPath As String)
    On Error Resume Next
    Dim fs As Object
    Dim folder As Object
    Dim subFolder As Object
    Dim file As Object
    ' 创建 FileSystemObject
    Set fs = CreateObject("Scripting.FileSystemObject")
    ' 获取目录对象
    Set folder = fs.GetFolder(folderPath)
    ' 遍历目录中的子目录并递归删除
    For Each subFolder In folder.SubFolders
        DeleteDirectory subFolder.Path
    Next subFolder
    ' 删除目录中的文件
    For Each file In folder.Files
        file.Delete
    Next file
    ' 删除目录
    folder.Delete
    ' 释放对象引用
    Set file = Nothing
    Set subFolder = Nothing
    Set folder = Nothing
    Set fs = Nothing
End Sub

3、读取OFD文件过程ReadOFDInvoiceInfo,代码也不少,同样我贴到第二条文章。

(1)检查有没有RAR压缩文件,有就取得其安装路径,没有就退出程序。这里定义了一个自定义函数GetRARPath,也是ChatGPT贡献的

Function GetRARPath()
    Dim rarPath As String
    On Error Resume Next
    ' 打开WinRAR的注册表项
    rarPath = CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe\")
    If Err.Number = 0 Then
        ' 获取WinRAR的安装路径
        GetRARPath = rarPath
    Else
        ' RAR未安装
        GetRARPath = ""
    End If
    On Error GoTo 0
End Function

(2)把OFD文件解压到临时文件夹下,这里通过shell函数执行RAR解压命令,由于解压需要点时间,所以用了个Sleep函数暂停1秒,不然没完全解压就执行后面的代码会报错,虽然后面Do Until~Loop也是起等待的作用,在原来只有一种发票格式的情况下可行,后来增加了一种发票就不太行了,有时会报错,这里以后再优化:

destFolder = tempFolder & Format(Time, "hhmmss") & "\"
rarCmd?=?rarPath?&?"?X?"?&?currInvoiceFile?&?"?"?&?destFolder
Result?=?shell(rarCmd,?vbHide)
Sleep?1000?'?Delay?for?2?seconds?(2000?milliseconds)
'等待解压完成
Do?Until?Dir(destFolder?&?"Doc_0\Attachs\original_invoice.xml")?<>?""?_
    Or Dir(destFolder & "Doc_0\Pages\Page_0\Content.xml") <> ""
        DoEvents
Loop

(3)读取发票信息

 NewDOMD.Load destFolder & "Doc_0\Attachs\original_invoice.xml"
?Set?xmld?=?NewDOMD.DocumentElement.SelectSingleNode("//eInvoice")
?Set?SelectedNode?=?xmld.SelectSingleNode("//fp:InvoiceCode")
?InvoiceCode?=?SelectedNode.Text
?
......


NewDOMD.Load destFolder & "Doc_0\Pages\Page_0\Content.xml"
Set?xmld?=?NewDOMD.DocumentElement.SelectSingleNode("//ofd:Page")
Set?SelectedNode?=?xmld.SelectSingleNode("//ofd:TextObject[@ID='6922']/ofd:TextCode")
InvoiceCode?=?Left(SelectedNode.Text,?12)
InvoiceNo?=?Right(SelectedNode.Text,?8)

代码解析:

(A) NewDOMD.Load加载XML文件

(B)Set xmld取得一个节点

(C)Set SelectedNode选中一个具体的节点

(D)InvoiceCode = SelectedNode.Text ,把节点TEXT赋值给变量。

(E)开头是旧式的电子发票,有密码区的,后面的最近的数电发票,两者的结构是不一样的,有兴趣的朋友可以找来这样的发票解压后分析。新式发票没有发票代码,发票号码是20位,这里我们还是把它处理成12位发票代码和8位发票号码。

其他一些过程什么的就不说了,上面的示例代码仅为部分代码,并且后续可能会修改,感兴趣的朋友可以参看下一篇文章。好,今天就到这吧,欢迎点赞、留言、分享,谢谢大家,我们下期再会。


☆猜你喜欢☆

【重磅】Excel VBA 应用分享/中医诊所收费系统/Excel+ListBox版

Excel VBA 动态添加控件/学生成绩筛选

Excel VBA 这样酷炫的日期控件,你不想要吗?

Excel 公式函数/数据透视表/固定资产折旧计提表!

Excel VBA 自定义函数/数组字段定位/数组字段排序

Excel 功能/公式函数/VBA/多种姿势处理重复值

Excel VBA 最简单的收发存登记系统

Excel 公式函数/查找函数之LOOKUP

Excel VBA 文件批量改名

Excel 公式函数/数据验证/动态下拉列表

Excel VBA 输入逐步提示/TextBox+ListBox

Excel 基础功能【数据验证】,你会怎么用?


本文于2023年5月31日首发于本人同名公众号:Excel活学活用,更多文章案例请搜索关注!

相关推荐

当 Linux 根分区 (/) 已满时如何释放空间?

根分区(/)是Linux文件系统的核心,包含操作系统核心文件、配置文件、日志文件、缓存和用户数据等。当根分区满载时,系统可能出现无法写入新文件、应用程序崩溃甚至无法启动的情况。常见原因包括:...

玩转 Linux 之:磁盘分区、挂载知多少?

今天来聊聊linux下磁盘分区、挂载的问题,篇幅所限,不会聊的太底层,纯当科普!!1、Linux分区简介1.1主分区vs扩展分区硬盘分区表中最多能存储四个分区,但我们实际使用时一般只分为两...

Linux 文件搜索神器 find 实战详解,建议收藏

在Linux系统使用中,作为一个管理员,我希望能查找系统中所有的大小超过200M文件,查看近7天系统中哪些文件被修改过,找出所有子目录中的可执行文件,这些任务需求...

Linux 操作系统磁盘操作(linux 磁盘命令)

一、文档介绍本文档描述Linux操作系统下多种场景下的磁盘操作情况。二、名词解释...

Win10新版19603推送:一键清理磁盘空间、首次集成Linux文件管理器

继上周四的Build19592后,微软今晨面向快速通道的Insider会员推送Windows10新预览版,操作系统版本号Build19603。除了一些常规修复,本次更新还带了不少新功能,一起来了...

Android 16允许Linux终端使用手机全部存储空间

IT之家4月20日消息,谷歌Pixel手机正朝着成为强大便携式计算设备的目标迈进。2025年3月的更新中,Linux终端应用的推出为这一转变奠定了重要基础。该应用允许兼容的安卓设备...

Linux 系统管理大容量磁盘(2TB+)操作指南

对于容量超过2TB的磁盘,传统MBR分区表的32位寻址机制存在限制(最大支持2.2TB)。需采用GPT(GUIDPartitionTable)分区方案,其支持64位寻址,理论上限为9.4ZB(9....

Linux 服务器上查看磁盘类型的方法

方法1:使用lsblk命令lsblk输出说明:TYPE列显示设备类型,如disk(物理磁盘)、part(分区)、rom(只读存储)等。...

ESXI7虚机上的Ubuntu Linux 22.04 LVM空间扩容操作记录

本人在实际的使用中经常遇到Vmware上安装的Linux虚机的LVM扩容情况,最终实现lv的扩容,大多数情况因为虚机都是有备用或者可停机的情况,一般情况下通过添加一块物理盘再加入vg,然后扩容lv来实...

5.4K Star很容易!Windows读取Linux磁盘格式工具

[开源日记],分享10k+Star的优质开源项目...

Linux 文件系统监控:用脚本自动化磁盘空间管理

在Linux系统中,文件系统监控是一项非常重要的任务,它可以帮助我们及时发现磁盘空间不足的问题,避免因磁盘满而导致的系统服务不可用。通过编写脚本自动化磁盘空间管理,我们可以更加高效地处理这一问题。下面...

Linux磁盘管理LVM实战(linux实验磁盘管理)

LVM(逻辑卷管理器,LogicalVolumeManager)是一种在Linux系统中用于灵活管理磁盘空间的技术,通过将物理磁盘抽象为逻辑卷,实现动态调整存储容量、跨磁盘扩展等功能。本章节...

Linux查看文件大小:`ls`和`du`为何结果不同?一文讲透原理!

Linux查看文件大小:ls和du为何结果不同?一文讲透原理!在Linux运维中,查看文件大小是日常高频操作。但你是否遇到过以下困惑?...

使用 df 命令检查服务器磁盘满了,但用 du 命令发现实际小于磁盘容量

在Linux系统中,管理员或开发者经常会遇到一个令人困惑的问题:使用...

Linux磁盘爆满紧急救援指南:5步清理释放50GB+小白也能轻松搞定

“服务器卡死?网站崩溃?当Linux系统弹出‘Nospaceleft’的红色警报,别慌!本文手把手教你从‘删库到跑路’进阶为‘磁盘清理大师’,5个关键步骤+30条救命命令,快速释放磁盘空间,拯救你...

取消回复欢迎 发表评论: