【前言】:经过一段时间的开发和试用,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