作为一名财务工作人员,每月月底,需要将当月财务部门多个开票员开具的发票信息进行汇总统计。该项工作其实难度不大,但由于开票量大,开票人数多,工作较为繁琐,只是复制粘贴的话总是会出现各种各样的问题,比如,Ctrl+C
Ctrl+V
的时候会同时复制大量的乱七八糟的单元格样式,造成表格很卡;复制粘贴为数值时小数点的位数出错等等。这些错误只有遇到的时候才知道有多么的头疼。所以,为了提高效率,准确率,想办法用python
实现自动化汇总才是王道。
作为财务的都知道开发票需要税控盘,刚好,税控盘里有开票资料导出功能,可以导出xls
格式和xml
格式。导出的表格有大量的空格,处理起来比较麻烦,所以选择使用xml
格式,同时利用python
的xml
库来读取数据。
从xml
文件里读取需要的数据之后,将其写入到xlsx
文件中,然后按需要整理格式及样式即可。
总体的思路如下:
1、逐个读取收集来的xml
中需要用到的发票信息,保存在一个变量中
2、将读取的发票信息按不同开票人和发票类型写入同一个工作簿的不同sheet
中,写入sheet的同时需要按发票开具的时间进行排序
3、将所有人的信息汇总到一张工作表中
1、逐个读取xml文件
from xml.dom.minidom import parseString
def get_xmlinfo(path):
"""
读取xml文件中的有用数据
"""
with open(path,encoding='GB2312') as f: # 导出的xml文件的编码格式为GB2312
datasource=f.read()
element = parseString(datasource).documentElement
nodes = element.getElementsByTagName('Fp') # 找到"Fp"节点
result = {}
for i in range(len(nodes)):
fp={} # 每个Fp节点是一张发票的信息,新建一个字典来保存需要的信息
sun_node = nodes[i].childNodes # Fp节点的子节点为Fpzl Lbdm等节点
for j in range(len(sun_node)): # 循环输出子节点的信息
if(sun_node[j].nodeName=="#text" or sun_node[j].nodeName=="Spxx"):
continue
try:
fp[sun_node[j].nodeName]=sun_node[j].firstChild.data
except:
fp[sun_node[j].nodeName]="" # 部分节点无数据,为避免报错,设置为空
result[fp["Kprq"]]=fp # 开票日期具体到时分秒,以此为主键可以避免重复,同时也方便后续排序
return result
2、写入excel文件
from openpyxl import Workbook
def writeexcel(wb,data):
"""
根据读取的xml文件的信息,按需要的格式写入到excel文件中
"""
rq=sorted(data.keys()) # 按开票日期进行排序
sheet = wb.create_sheet(data[rq[0]]['Fpzl']+"-"+data[rq[0]]['Kpr']) # 按发票类型和开票人定义sheet名
head = ('Kprq','Gfmc','Lbdm','Fphm','Hjje','Hjse','Jshj','Sl','Kpr','Fpzl','Fpzt')
# 设置表头
sheet.append(head)
i=2
for kpsj in rq:
sheet.cell(row=i, column=head.index('Kprq') + 1).value = data[kpsj]['Kprq']
sheet.cell(row=i, column=head.index('Gfmc') + 1).value = data[kpsj]['Gfmc']
sheet.cell(row=i, column=head.index('Lbdm') + 1).value = data[kpsj]['Lbdm']
sheet.cell(row=i, column=head.index('Fphm') + 1).value = data[kpsj]['Fphm']
sheet.cell(row=i, column=head.index('Kpr') + 1).value = data[kpsj]['Kpr']
sheet.cell(row=i, column=head.index('Fpzl') + 1).value = data[kpsj]['Fpzl']
sheet.cell(row=i, column=head.index('Fpzt') + 1).value = data[kpsj]['Fpzt']
if data[kpsj]['Fpzt']!="填开作废" and data[kpsj]['Fpzt']!="空白作废":
sheet.cell(row=i, column=head.index('Hjje') + 1).value = float(data[kpsj]['Hjje'])
sheet.cell(row=i, column=head.index('Hjse') + 1).value = float(data[kpsj]['Hjse'])
sheet.cell(row=i, column=head.index('Jshj') + 1).value = round(float(data[kpsj]['Hjse']) + float(data[kpsj]['Hjje']),2)
sheet.cell(row=i, column=head.index('Sl') + 1).value = '{:.0%}'.format(float(data[kpsj]['Hjse'])/float(data[kpsj]['Hjje']))
else:
# 作废发票的金额税额价税合计填0
sheet.cell(row=i, column=head.index('Hjje') + 1).value = 0
sheet.cell(row=i, column=head.index('Hjse') + 1).value = 0
sheet.cell(row=i, column=head.index('Jshj') + 1).value = 0
sheet.cell(row=i, column=head.index('Sl') + 1).value = '0%'
i=i+1
3、多sheet汇总
import os
import openpyxl
import pandas as pd
def merge_sheet(excel_file_name):
"""
汇总多个工作表sheet至汇总工作表
只要是同样的需求该函数都可用,不只局限于该场景
"""
summary_sheet = None
reader = pd.ExcelFile(excel_file_name)
for sheet_name in reader.sheet_names:
sheet = reader.parse(sheet_name=sheet_name, dtype=str,header=0) # 循环读取每一个sheet,忽略第一行标题
# 加上dtype=str参数可以避免发票代码,发票号码等需要以文本格式存储的数字,变成纯数字导致前面的0丢失。
if summary_sheet is None:
summary_sheet = sheet
else:
summary_sheet = pd.concat([summary_sheet, sheet], ignore_index=True)
old_name, _ = os.path.splitext(excel_file_name)
writer = pd.ExcelWriter(old_name + "-合并.xlsx", engine="openpyxl")
wb = openpyxl.load_workbook(filename=excel_file_name)
writer.book = wb
# 需要汇总表与明细表在同一个工作簿,加上上面两句,不加则合并表格只有汇总sheet
summary_sheet.to_excel(writer, sheet_name="汇总", index=False) # 不需要索引
writer.save()
return None
4、批量处理
def searchWithSuffixxes(base_path,suffixes):
"""
查找当前路径之下所有的特定后缀(suffixes)的文件
返回 指定类型的文件路径 列表
"""
paths = []
for root,dirs,names in os.walk(base_path):
for name in names:
if name.endswith(suffixes):
paths.append(os.path.join(root,name))
return paths
if __name__=="__main__":
# 遍历当前路径下所有的xml文件,逐一调用上述函数
base_path = os.getcwd()
all_files = searchWithSuffixxes(base_path,".xml")
wb = Workbook()
for file in all_files:
try:
xml_info=get_xmlinfo(file)
writeexcel(wb,xml_info)
except:
continue
del wb['Sheet']
file_name = os.path.join(base_path,"发票明细"+datetime.datetime.now().strftime('%Y%m%d%H%M%S')+".xlsx")
wb.save(file_name)
merge_sheet(file_name)