【前言】:经过一段时间的开发和试用,farpy的demo版基本成形。
【far】:financial analysis reporter的缩写
【场景】:需要对某个公司的财务情况进行分析,demo版只是协助完成一些财务分析模板的搭建,主要运用在银行客户经理撰写尽职调查报告时,有效的减少一个重复性的文字输入,可以节省时间在真正核心的分析中。
【后续】:后续将进一步完善财务分析的智能分析部分,财务指标是死的,分析结论才是活的,才是真正体现财务分析人员水平的地方。
【requirement】:
docxtpl==0.6.3et-xmlfile==1.0.1jdcal==1.4.1Jinja2==2.10.3lxml==4.4.1MarkupSafe==1.1.1openpyxl==3.0.0python-docx==0.8.7vnpy==1.9.2xlrd==1.2.0xlwt==1.3.0

1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 #__author__:"watalo"
4 #date: 2019/10/26
5
6 '''
7 1、固定格式的下列xlsx放在input文件夹中
8 2、读取数据进入列表
9 3、对列表数据进行文本操作
10 '''
11
12 from docx import Document
13 from docx.oxml.ns import qn
14 from openpyxl import load_workbook
15 import os
16 import rpt_module
17
18
19 #全局变量声明
20 name = 'XXX股份有限公司'
21
22 #科目变量
23 items = []
24 date2y = []
25 date1y = []
26 date0y = []
27
28 #路径变量
29 rootpath = os.path.abspath(os.path.join(os.getcwd(),"..")) # reporter.py的上一级目录,目录下有input文件夹,
30 datapath = ''.join([rootpath,r"\input\data.xlsx"])# 读取财务数据的xlsx格式文件
31
32 #读取数据
33 '''将固定格式的数据导入列表,列表就是长度为4的时间序列,列表[0]是变量名,后面是数据'''
34 wb = load_workbook(filename = datapath)
35 ws = wb["Sheet1"]
36 year2 = ws.cell(1,2).value
37 year1 = ws.cell(1,3).value
38 year0 = ws.cell(1,4).value
39 for row in ws.iter_rows(min_row=2,max_col=4,max_row=35):
40 item = row[0].value
41 items.append(str(item))
42 date2 = float(row[1].value)
43 date2y.append(date2)
44 date1 = float(row[2].value)
45 date1y.append(date1)
46 date0 = float(row[3].value)
47 date0y.append(date0)
48
49 #编写报告
50 #1、财务简表
51 document = Document()
52 document.styles['Normal'].font.name = u'宋体'
53 document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
54 head1 = '1、财务简表'
55 run = document.add_heading('',level=1).add_run(head1)
56 run.font.name=u'宋体'
57 run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
58
59 rows = list(ws.rows)
60 table = document.add_table(rows = len(rows),
61 cols = len(rows[0]),
62 style = "Medium Shading 1 Accent 1")
63 for irow, row in enumerate(rows):
64 for icols, cols in enumerate(row):
65 if type(cols.value) == str:
66 table.cell(irow, icols).text = str(cols.value)
67 else:
68 i = round(float(cols.value),2)
69 table.cell(irow,icols).text = str(i)#多出来一列,不知道怎么处理
70 #初步判断,应该是后面一列出现了格式设置,但没有内容,不过openpyxl还是认定这列有内容,返回None值。
71
72 #2、整体分析
73 head2 = '2、整体评价'
74 run = document.add_heading('',level=1).add_run(head2)
75 run.font.name=u'宋体'
76 run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
77
78 #偿债能力
79 text_modle2 = "截至%s末,该公司总资产%.2f万元,总负债%.2f万元,资产负债率%.2f%%,流动资产%.2f万元," \
80 "流动负债%.2f万元,流动比率%.2f,速动比率%.2f,长期偿债能力%s,短期偿债能力%s,【%s】。"
81 year = year0
82 total_asset = float(ws.cell(2,4).value)
83 total_debt = float(ws.cell(17,4).value)
84 asset_liability_ratio = float(100*total_debt/total_asset)
85 liquid_asset = float(ws.cell(3,4).value)
86 liquid_debt = float(ws.cell(18,4).value)
87 rate_of_liquid = float(ws.cell(61,4).value)
88 rate_of_speed = float(ws.cell(62,4).value)
89
90 long_slovency = ""
91 if asset_liability_ratio >= 70:
92 long_slovency = "较弱"
93 elif asset_liability_ratio >= 50 and asset_liability_ratio < 70:
94 long_slovency = "一般"
95 elif asset_liability_ratio >= 0 and asset_liability_ratio < 50:
96 long_slovency = "较强"
97 else:
98 long_slovency = ""
99
100 short_slovency = ""
101 if rate_of_liquid >= 1 and rate_of_speed >= 1:
102 short_slovency = "较强"
103 elif rate_of_liquid >= 1 and rate_of_speed < 1:
104 short_slovency = "一般"
105 elif rate_of_liquid < 1 and rate_of_speed < 1:
106 short_slovency = "较弱"
107 else:
108 short_slovency = ""
109
110 all_commits = rpt_module.commits(long_slovency,short_slovency)
111
112 text_set2 = (year, total_asset, total_debt, asset_liability_ratio, liquid_asset, liquid_debt, rate_of_liquid, rate_of_speed,
113 long_slovency, short_slovency, all_commits)
114 document.add_paragraph(text_modle2%text_set2)
115
116 #盈利能力
117 # text_modle3 = ""
118 # 科目变化的表格展示
119 # 变化超过30%的科目
120 # document.add_table(c)
121 # for i in range(34):
122 # if abs(rpt_module.data_format(date0y,date1y))>30:
123
124
125 #3、科目分析
126 head3 = '3、根据审计报告附注及尽职调查对【期末】主要财务数据分析'
127 run = document.add_heading('',level=1).add_run(head3)
128 run.font.name=u'宋体'
129 run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
130
131 #函数
132 def item_table(colu1, colu2, colu3, colu4):
133 '''
134 需要导入python-docx
135 科目明细的表格制作,分为几种不同特点的表格体系:
136 detail:科目明细前五名(6rows 4columns)
137 :param style: 目前就这2个选项 var detail
138 :return: 表格样式
139 '''
140 table_regular = [colu1, colu2, colu3, colu4]
141 table = document.add_table(rows=7, cols=4, style='Medium Shading 2 Accent 1')
142 for i in range(4):
143 cell = table.cell(0, i)
144 cell.text = table_regular[i]
145 for i in range(5):
146 cell = table.cell(i + 1, 0)
147 cell.text = str(i + 1)
148
149
150 def para_format(items,date1y,date0y,type):
151 global liquid_asset
152 zf = rpt_module.data_format(date0y, date1y)
153 if type == "sa":
154 text_modle = "【%s】:%.2f万元,流动资产占比%.2f%%,较上年增加%.2f万元,增幅%.2f%%。"
155 item_in_sa = float(100*date0y/liquid_asset)
156 text_set = (items, date0y, item_in_sa, date0y - date1y, zf)
157 return text_modle % text_set
158 elif type == "sd":
159 if total_asset-liquid_asset == 0:
160 pass
161 else:
162 text_modle = "【%s】:%.2f万元,流动负债占比%.2f%%,较上年增加%.2f万元,增幅%.2f%%。"
163 item_in_sd = float(100*date0y/liquid_debt)
164 text_set = (items, date0y, item_in_sd, date0y - date1y, zf)
165 return text_modle % text_set
166 elif type == "la":
167 text_modle = "【%s】:%.2f万元,非流动资产占比%.2f%%,较上年增加%.2f万元,增幅%.2f%%。"
168 item_in_la = float(100*date0y/(total_asset-liquid_asset))
169 text_set = (items, date0y, item_in_la, date0y - date1y, zf)
170 return text_modle % text_set
171 elif type == "ld":
172 if total_debt-liquid_debt == 0:
173 pass
174 else:
175 text_modle = "【%s】:%.2f万元,非流动负债占比%.2f%%,较上年增加%.2f万元,增幅%.2f%%。"
176 item_in_ld = float(100*date0y/(total_debt-liquid_debt))
177 text_set = (items, date0y, item_in_ld, date0y - date1y, zf)
178 return text_modle % text_set
179 else:
180 pass
181
182 '''对财务报表的各个科目进行格式化输出,明细内容需要调查后填写'''
183 for i in range(34): #对33个财务科目进行分析
184 if items[i] == "货币资金":
185 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
186 regular = para_format(items[i],date1y[i],date0y[i],"sa")
187 paragraph = document.add_paragraph(regular)
188 paragraph.add_run("其中现金【】万元,银行存款【】万元、其他货币资金【】万元。")
189 else:
190 pass
191
192 elif items[i] == "应收票据":
193 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
194 regular = para_format(items[i], date1y[i], date0y[i],"sa")
195 paragraph = document.add_paragraph(regular)
196 paragraph.add_run("其中银行承兑汇票【】万元,商业承兑汇票【】万元。")
197 else:
198 pass
199
200 elif items[i] == "应收账款":
201 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
202 regular = para_format(items[i], date1y[i], date0y[i],"sa")
203 paragraph = document.add_paragraph(regular)
204 paragraph.add_run("账面余额【】万元、计提坏账准备【】万元,账龄1年以内占比【】%,3年以上占比【】%。其中,应收账款前五位:")
205 item_table("序号", "名称", "余额(万元)", "占比")
206 else:
207 pass
208
209 elif items[i] == "预付账款":
210 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
211 regular = para_format(items[i], date1y[i], date0y[i],"sa")
212 paragraph = document.add_paragraph(regular)
213 paragraph.add_run("账龄1年以内占比【】%,3年以上占比【】%。其中,预收账款前五位:")
214 item_table("序号", "名称", "余额(万元)", "占比")
215 else:
216 pass
217
218 elif items[i] == "其他应收款":
219 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
220 regular = para_format(items[i], date1y[i], date0y[i],"sa")
221 paragraph = document.add_paragraph(regular)
222 paragraph.add_run("账面余额【】万元、计提坏账准备【】万元。其中,其他应付款前五位:")
223 item_table("序号", "名称", "余额(万元)", "款项性质")
224 else:
225 pass
226
227 elif items[i] == "存货":
228 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
229 regular = para_format(items[i], date1y[i], date0y[i],"sa")
230 paragraph = document.add_paragraph(regular)
231 paragraph.add_run("其中,原材料【】万元、库存商品【】万元、周转材料【】万元、工程施工【】万元、开发成本【】万元。")
232 else:
233 pass
234
235 elif items[i] == "其他流动资产":
236 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
237 regular = para_format(items[i], date1y[i], date0y[i],"sa")
238 paragraph = document.add_paragraph(regular)
239 paragraph.add_run("其中【添加明细】。")
240 else:
241 pass
242
243 elif items[i] == "长期股权投资":
244 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
245 regular = para_format(items[i], date1y[i], date0y[i], "la")
246 paragraph = document.add_paragraph(regular)
247 paragraph.add_run("系对【填数字】家企业的投资,本期主要新增【哪家公司】;对外投资前五位如下:")
248 item_table("序号", "名称", "投资额", "投资性质")
249 else:
250 pass
251
252 elif items[i] == "固定资产":
253 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
254 regular = para_format(items[i], date1y[i], date0y[i], "la")
255 paragraph = document.add_paragraph(regular)
256 paragraph.add_run("固定资产净值【】万元,累计折旧【】万元,其中房屋及建筑物【】万元、机器设备【】万元、办公设备【】万元、【其他】【】万元。")
257 else:
258 pass
259
260 elif items[i] == "在建工程":
261 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
262 regular = para_format(items[i], date1y[i], date0y[i], "la")
263 paragraph = document.add_paragraph(regular)
264 paragraph.add_run("主要为【项目1】【】万元、【项目2】【】万元、【项目3】【】万元……")
265 else:
266 pass
267
268 elif items[i] == "无形资产":
269 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
270 regular = para_format(items[i], date1y[i], date0y[i], "la")
271 paragraph = document.add_paragraph(regular)
272 paragraph.add_run("主要为土地使用权【】万元、采矿权【】万元、专利权【】万元、软件【】万元,其他【】万元。")
273 else:
274 pass
275
276 elif items[i] == "短期借款":
277 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
278 regular = para_format(items[i], date1y[i], date0y[i], "sd")
279 paragraph = document.add_paragraph(regular)
280 paragraph.add_run("主要为【XX银行】【】万元、【XX银行】【】万元、【XX银行】【】万元、【xx银行】【】万元、【xx银行】【】万元。【其他需要说明的内容】")
281 else:
282 pass
283
284 elif items[i] == "应付票据":
285 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
286 regular = para_format(items[i], date1y[i], date0y[i], "sd")
287 paragraph = document.add_paragraph(regular)
288 paragraph.add_run("主要为银行承兑汇票【】万元,商业承兑汇票【】万元。")
289 else:
290 pass
291
292 elif items[i] == "应付账款":
293 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
294 regular = para_format(items[i], date1y[i], date0y[i], "sd")
295 paragraph = document.add_paragraph(regular)
296 paragraph.add_run("其中应付材料款【】万元,应付工程款【】万元。其中前五名如下:")
297 item_table("序号", "名称", "余额", "性质")
298 else:
299 pass
300
301 elif items[i] == "预收账款":
302 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
303 regular = para_format(items[i], date1y[i], date0y[i], "sd")
304 paragraph = document.add_paragraph(regular)
305 paragraph.add_run("其中前5名如下:")
306 item_table("序号", "名称", "余额", "账龄")
307 else:
308 pass
309
310 elif items[i] == "其他应付款":
311 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
312 regular = para_format(items[i], date1y[i], date0y[i], "sd")
313 paragraph = document.add_paragraph(regular)
314 paragraph.add_run("应付利息【】万元,往来款【】万元,押金和保证金【】万元,其中前5名如下:")
315 item_table("序号", "名称", "余额", "账龄")
316 else:
317 pass
318
319 elif items[i] == "其他流动负债":
320 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
321 regular = para_format(items[i], date1y[i], date0y[i], "sd")
322 paragraph = document.add_paragraph(regular)
323 paragraph.add_run("主要为【】")
324 else:
325 pass
326
327 elif items[i] == "长期借款":
328 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
329 regular = para_format(items[i], date1y[i], date0y[i], "ld")
330 paragraph = document.add_paragraph(regular)
331 paragraph.add_run("主要为【XX银行】【】万元、【XX银行】【】万元、【XX银行】【】万元、【xx银行】【】万元、【xx银行】【】万元。【其他需要说明的内容】")
332 else:
333 pass
334
335 elif items[i] == "应付债券":
336 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
337 regular = para_format(items[i], date1y[i], date0y[i], "ld")
338 paragraph = document.add_paragraph(regular)
339 paragraph.add_run("主要为【】")
340 else:
341 pass
342
343 elif items[i] == "长期应付款":
344 if date2y[i] != 0 or date1y[i] != 0 or date0y[i] != 0:
345 regular = para_format(items[i], date1y[i], date0y[i], "ld")
346 paragraph = document.add_paragraph(regular)
347 paragraph.add_run("其中专项应付款【】万元、【】【】万元、其他【】万元。")
348 else:
349 pass
350
351 document.save("%s财务分析.docx"%name)
有兴趣共同开发的可以上github关注我,https://github.com/watalo/farpy
