感谢采悟老师分享的账龄分析的度量值写法

原文链接: Power BI财务分析应用:应收账款账龄分析 - 知乎 (zhihu.com)

本文在此基础上,再作一些拓展

  1. 使之可以针对不同的过去时间点,分别显示当时的账龄分布情况。
  2. 计算未收到账款的平均日龄、最大日龄1
  3. 展示一步步尝试与修改,将看似复杂困难的问题分解为一系列容易实施的多步骤的过程。

[! 示例文件下载:]
百度网盘
提取码:ucwz
应收账款明细数据源由 python 代码虚构。虚构一份应收账款的明细账,为制作Power BI_芦骁骏的博客-CSDN博客

一、参考采悟老师的分享,建立度量值计算账龄,并扩展其功能

1. 建立度量值,使之能计算期末余额的来源

应收发生额 = SUM('应收账款明细'[借方金额])
应收未收金额.未分客户 =
var maxDate=CALCULATE(MAX('Date'[日期]),ALLSELECTED('Date'))
var endAmt=CALCULATE(SUM('应收账款明细'[金额]),'Date'[日期]<=maxDate)
var a=[应收发生额]
var b=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],MIN('Date'[日期]),maxDate))
var c=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],max('Date'[日期])+1,maxDate))
return SWITCH(true(),b<=endAmt,a,b>endAmt,max(0,endAmt-c)
)
应收未收金额.分客户 =
SUMX(VALUES('客户'[客户]),[应收未收金额.未分客户]
)

[!注意] 必须要分客户进行计算,否则会因不同客户间的应收款与实收款项的抵消,造成错误
可以未分客户的度量值,嵌套进 SUMX(VALUES('客户'[客户]),...) 解决。

使用以上度量值,可以看到期末各客户未付款的金额,及其这些款账产生的月份。

2. 引用以上度量值,建立计算账龄分组的度量值

应收账款.按账龄分组.错误版 =
var maxDate=max('Date'[日期])
var lowLimit=MIN('账龄分组2'[下限])
var highLimit=MAX('账龄分组2'[上限])
var beginDate=maxDate-highLimit
var endDate=maxDate-lowLimit
var groupedAmt=CALCULATE([应收未收金额.未分客户],DATESBETWEEN('Date'[日期],beginDate,endDate)
)
return groupedAmt
应收账款.按账龄.分客户.错误版 =
var x=SUMX(VALUES('客户'[客户]),[应收账款.按账龄分组.错误版])
return if(x<>0,round(x,2))

查看结果会发现,存在明显的错误。客户在各账龄分组的金额与总的金额相差甚远。其原因是:[应收账款. 按账龄分组. 错误版] 度量值中引用到了 [应收未收金额.未分客户]。而 [应收未收金额.未分客户] 中有 var maxDate=CALCULATE(MAX('Date'[日期]),ALLSELECTED('Date')) 定义的 maxDate

maxDate 在上面的表中计算期末余额来源时能正确计算出 2022-12-31

maxDate[应收账款.按账龄分组.错误版] 的计算中,由于外层套着 CALCULATE(....,DATESBETWEEN(...)),其会影响到 maxDate 的计算环境,使其计算结果为 DATESBETWEEN(...) 中的最大值,endDate

3. 修正上述错误

因为 ALLSELECTED 函数的一些固有限制,嵌套在最里的它,在这里是无法正确取到我们想要那一层筛选上下文中的最大日期的。为了解决上面的错误,我们选择将 [应收未收金额.未分客户] 的代码直接写进 [应收账款.按账龄分组] 内,并将 maxDate 提到 CALCULATE(....,DATESBETWEEN(...)) 之外。

修改后的代码如下:

应收账款.按账龄分组.修正 =
var maxDate=max('Date'[日期])
var lowLimit=MIN('账龄分组2'[下限])
var highLimit=MAX('账龄分组2'[上限])
var beginDate=maxDate-highLimit
var endDate=maxDate-lowLimit
var groupedAmt=CALCULATE(var endAmt=CALCULATE(SUM('应收账款明细'[金额]),'Date'[日期]<=maxDate)var a=[应收发生额]var b=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],MIN('Date'[日期]),maxDate))var c=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],max('Date'[日期])+1,maxDate))return SWITCH(true(),b<=endAmt,a,b>endAmt,max(0,endAmt-c)),DATESBETWEEN('Date'[日期],beginDate,endDate)
)
return groupedAmt
应收账款.按账龄.分客户.修正 =
var x=SUMX(VALUES('客户'[客户]),[应收账款.按账龄分组.修正])
return if(x<>0,round(x,2))

使用以上代码已能基础正确计算账龄分组,但还是有个小错误。如下图第一行,总计的金额与 1-2 个月的金额相等,而分组中1 个月以内也还有金额。而下面的行则没有这个错误。
其原因是正好有一笔应收款,既分类到1 个月以内又分类到1-2 个月了。

var maxDate=max('Date'[日期])
var lowLimit=MIN('账龄分组2'[下限])
var highLimit=MAX('账龄分组2'[上限])
var beginDate=maxDate-highLimit
var endDate=maxDate-lowLimit

看这段代码,如果 lowLimithighLimit 相差 30 。比如说是 0 和 30 。则 beginDateendDate 也会相差 30 。DATESBETWEEN 函数会将两端都保留,一共会是 31 个日期。这与我们设计的分组规则不符。显然,最适一天,也就是数值 0,我们是要保留的。所以,只要将 beginDate 加 1 。将其代码改为 var beginDate=maxDate-highLimit

已得正确结果:

4. 将客户字段换成日期表中的字段, 也能正确计算。

比如换成年或年月、日期,也能正确计算在以前的时间结点上的账龄分布情况。借此,我们可以做图或表查看变化趋。发现一些异常的变化。

二、计算平均日龄、最大日龄

1.针对一个客户一个日期点,在度量值中先建立一张表,包括每笔未收到的应收账款的产生日期和金额

由于在 PBI 的度量值结果不能是一个表格,我们选择先在 DAX Studio 中进行尝试。选一个在某日(比如 2021-12-31)有数据的客户,建立一张上述的表格。

EVALUATE
CALCULATETABLE(ADDCOLUMNS(VALUES('Date'[日期]),"应收未收金额",[应收未收金额.未分客户]),'Date'[日期]=date(2021,12,31),'客户'[客户]="Arias-Erickson"
)

选择"Arias-Erickson"在 2021-12-31,简单地建一个表格,尝试一下。

结果不对,原因是因为 VALUES('Date'[日期])'Date'[日期]=date(2021,12,31) 影响,只有一个值。在这里只能使用 ALL 并加上筛选,筛选早于 date() 中指定的值。

EVALUATE
CALCULATETABLE(ADDCOLUMNS(filter(ALL('Date'[日期]),'Date'[日期]<=date(2021,12,31)),"应收未收金额",[应收未收金额.未分客户]),'Date'[日期]=date(2021,12,31),'客户'[客户]="Arias-Erickson"
)


结果中空行太多,加一步筛选:

EVALUATE
CALCULATETABLE(FILTER(ADDCOLUMNS(filter(ALL('Date'[日期]),'Date'[日期]<=date(2021,12,31)),"应收未收金额",[应收未收金额.未分客户]),[应收未收金额]<>0  ),'Date'[日期]=date(2021,12,31),'客户'[客户]="Arias-Erickson"
)

得到结果:

与视觉对象中显示的相同。(这里查看是在 21 年末的客户账龄,注意筛选条件中不要选到 2022 年)

2. 利用该表计算出一个客户在一个日期的平均日龄

调试后的代码如下:

EVALUATE
CALCULATETABLE(var maxDate=max('Date'[日期])var restTable=FILTER(ADDCOLUMNS(filter(ALL('Date'[日期]),'Date'[日期]<=date(2021,12,31)),"应收未收金额",[应收未收金额.未分客户]),[应收未收金额]<>0  )var tableAddDays=ADDCOLUMNS(restTable,"日龄",VALUE(maxDate-'Date'[日期]))var avgDays=DIVIDE(SUMX(tableAddDays,[应收未收金额]*[日龄]),SUMX(tableAddDays,[应收未收金额]))return{avgDays},'Date'[日期]=date(2021,12,31),'客户'[客户]="Arias-Erickson"
)

在调试过程中,如果遇到错误,可以通过修改 return 后的内容,将相应的中应过程输出进行查看。如果输出的不是表而是值,需要使用花括号包裹。因为DAX StudioEvaluate 是用于计算表格的。比如:tableAddDays 的具体数据:

以上代码:可以已经正确输出计算结果。

3. 尝试改进上述代码,使其能够适应不同客户在不同日期的平均日龄计算

最终的度量值需要放到如下图所示的矩阵中进行计算。

我们可以继续在DAX Studio进行调试:
calculate( ...,'Date'[日期]=date(2021,12,31),'客户'[客户]="Arias-Erickson") 这部分分离出去,使用 crossjoin(客户,日期) 构建完整的客户与日期的组合。

将另外部分使用 define 定义为一个DAX Studio中的度量值。

初步将代码修改如下:

DEFINE Measure
'00DAX'[myMeasure]=var maxDate=max('Date'[日期])var restTable=FILTER(ADDCOLUMNS(filter(ALL('Date'[日期]),'Date'[日期]<=maxDate //替换掉原先的date(2021,12,31)),"应收未收金额",[应收未收金额.未分客户]),[应收未收金额]<>0  )var tableAddDays=ADDCOLUMNS(restTable,"日龄",VALUE(maxDate-'Date'[日期]))var avgDays=DIVIDE(SUMX(tableAddDays,[应收未收金额]*[日龄]),SUMX(tableAddDays,[应收未收金额]))returnavgDaysEVALUATE
var daysTable=ADDCOLUMNS(CROSSJOIN(VALUES('Date'[年序号]),VALUES('客户'[客户])),"myMeasure",[myMeasure])
var daysTableFilter=FILTER(daysTable,[myMeasure]<>0)
return daysTableFilter

显然,计算结果还不正确。这里的错误原因,与上文一、2. 中的错误原因一样。因为在计算 restTable 时用了度量值 [应收未收金额.未分客户] 该度量值中有一段 var maxDate=CALCULATE (MAX ('Date'[日期]), ALLSELECTED ('Date')) 。修改的方法,是要让它直接使用外面定好的 maxDate

4. 修正错误

将上图中的 [应收未收金额.未分客户] 替换为如下代码:

var endAmt=CALCULATE(SUM('应收账款明细'[金额]),'Date'[日期]<=maxDate)
var a=[应收发生额]
var b=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],'Date'[日期],maxDate))
var c=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],'Date'[日期]+1,maxDate))
return
IF('Date'[日期]<=maxDate,SWITCH(true(),b<=endAmt,a,b>endAmt,max(0,endAmt-c))
)

这段代码中原先有用 var 定义 maxDate。但这里要用外层定义好的 maxDate,所以直接省略掉就好。这样已经可以得到正确的计算结果。

5. 将上文中 define 内定义的度量值写到 PBI 中

得到最终的度量值:

平均日龄 =
var maxDate=max('Date'[日期])
var restTable=FILTER(ADDCOLUMNS(filter(ALL('Date'[日期]),'Date'[日期]<=maxDate),"应收未收金额",var endAmt=CALCULATE(SUM('应收账款明细'[金额]),'Date'[日期]<=maxDate)var a=[应收发生额]var b=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],'Date'[日期],maxDate))var c=CALCULATE( [应收发生额],DATESBETWEEN('Date'[日期],'Date'[日期]+1,maxDate))returnIF('Date'[日期]<=maxDate,SWITCH(true(),b<=endAmt,a,b>endAmt,max(0,endAmt-c)))),[应收未收金额]<>0  )
var tableAddDays=ADDCOLUMNS(restTable,"日龄",VALUE(maxDate-'Date'[日期]))
var avgDays=DIVIDE(SUMX(tableAddDays,[应收未收金额]*[日龄]),SUMX(tableAddDays,[应收未收金额]))
returnavgDays

[!note]
return 后的结果,如果在DAX Studio中有花括号包裹,在 PBI 里则要去掉。度量值在DAX Studio中返回表,度量值要加花括号变成一个值的表;在 PBI 中返回值,要将花括号去掉。

即可使用矩阵查看各客户在不同日期的应收账款平均日龄。如果放入的字段是月份、年份之类的期间,则计算的该期间期末的数据。

6. 将上述代码稍作修改,即可得到度量值 [最大日龄]

具体内容见下载附件。


  1. 指没客户没付款的各笔款项距离统计日的天数, 统计日是列标题所指月份的最后一天 ↩︎

使用PBI进行应收账款分析相关推荐

  1. 需求分析模板_看完总经理做的公司财务经营分析报告,怪不得人家能拿年薪150万...

    一次偶然的机会,小编有幸参加了公司总经理召开的会议,他做的公司财务经营分析报告很详尽的描述了公司的现状和未来发展规划,听得我也是热血澎湃!看着公司业绩突飞猛进,利润持续增长,我心想怪不得人家能拿150 ...

  2. 中国卫生材料及医药用品行业发展前景与投资战略规划分析报告2022-2028年

    中国卫生材料及医药用品行业发展前景与投资战略规划分析报告2022-2028年 详情内容请咨询鸿晟信合研究院! [全新修订]:2022年2月 [撰写单位]:鸿晟信合研究研究 [报告目录] 第一章卫生材料 ...

  3. 数据可视化分析教学课件——FineBI实验册节选====库存与账款分析

      数据可视化分析课程教学,0基础也能掌握,本节讲述的是机械制造与自动化等专业的数据可视化分析案例:库存与账款分析 a.实验背景   1.库存结构不合理,滞销.脱销等问题频发:   2.库存周转率持续 ...

  4. 中国胶印机市场运行调研及投资前景预测分析报告2022-2028年

    中国胶印机市场运行调研及投资前景预测分析报告2022-2028年 ═━┈┈━══━┈┈━══━┈┈━══━   [出版机构]:  中商经济研究网 第一章 胶印机相关概述 12 第一节 胶印机特点 12 ...

  5. 看清上市公司的财务分析

    看清上市公司的财务分析 看清上市公司的财务分析是十分重要的,特别是用来评价上市公司,因此各位股民一定要学会基本会看这些东西哦 一)偿债能力分析 常用的偿债能力分析指标有流动比率.速动比率和资产负债率. ...

  6. 用函数计算工龄_用Excel计算财务账期,离不开这3个函数

    正文共:1577 字 6 图 预计阅读时间: 4 分钟 在很多企业,应收账款要按指定的账期显示,类似"0-30天,31-60天,--"这种样式.在Excel应收账款分析表中,需要根 ...

  7. 月薪2w的00后女财务,靠这套报表模板征服了老板

    只要学过一点财务的都知道,干财务迟早秃头,财务分析有许多套路和指标,纷繁复杂,外行人很难看懂.很多财务刚来的时候,手上拿着中级.ACCA.CPA等证书,甚至写不出一份老板满意的财务分析报告.所以,财务 ...

  8. 彻底告别“人工+Excel”低效模式,传统制造业实现“一站式”数据化管理

    本文为"帆软2018数据生产力大赛"参赛案例,未经授权,请勿转载! 引言 我国传统制造业一直是我国经济发展的支柱型产业,改革开放以来,我国传统制造业一直是以简单粗放.高耗能的劳动密 ...

  9. 如何利用FineBI做财务分析

    很多企业随着业务规模的增长,传统的财务分析方式采用手工摘取数据的方式,难以快速地对企财务经营状况作出及时分析和预测.现在业务人员通过使用自助式BI工具做财务分析已经成为流行,每个人都希望自己做报表,快 ...

最新文章

  1. bool转nsnumber ios_iOS开发之NSDecimalNumber的使用,货币计算/精确数值计算/保留位数等...
  2. win10 LTSC系统 安装应用商店和纸牌合集,解决从应用商店安装Solitaire Collection纸牌打开空白的问题
  3. 在mysql的操作界面中,如何清屏幕
  4. IDEA集成MAVEN 报错
  5. Linux内核模块(一)
  6. php数组教程,PHP 数组入门教程小结
  7. 植物大战僵尸c语言编程,c语言----实战植物大战僵尸
  8. iMeta封面 | 宏蛋白质组学分析一站式工具集iMetaLab Suite(加拿大渥太华大学Figeys组)...
  9. 用python计算狗的年龄_狗狗年龄与人的年龄的对比:狗的年龄:一年相当于人类的几岁...
  10. 【JS】V8 引擎原理
  11. Apollo6.0_ReferenceLine_Smoother解析与子方法对比
  12. Codeforces869C The Intriguing Obsession
  13. 【新智元峰会】德国AI教皇盛赞中国人工智能,25位AI领袖强势打造中国新智极...
  14. NET Core 模板项目 - NuGet
  15. 山东高考六选三学计算机选什么,山东省高中6选3选科数据出炉!和预想完全不一样,其他省区可参考!...
  16. UiPath的3个主要工具介绍
  17. Makefile185 recipe for target ‘objnetwork-kernels
  18. 强化学习笔记-马尔可夫决策过程
  19. ggplot绘制柱状图 python_ggplot2堆积柱形图笔记
  20. 五笔和拼音输入法开发

热门文章

  1. 数据结构清华大学公开课
  2. 计算机毕业设计Android汽车违章查询app(源码+系统+mysql数据库+Lw文档)
  3. 云端智创 | 基于视频AI原理的音视频智能处理技术
  4. SEO人员,如何打好免费流量与付费流量的组合拳?
  5. val()和.value的区别和用法
  6. Oracle分布式数据库的垂直分片实例
  7. 大学计算机重修有什么影响,大学重修会有什么影响
  8. 捷联惯导系统学习6.2(序贯滤波 )
  9. matlab 序贯相似性,序贯相似性检测算法(SSDA)实现图像匹配
  10. 【2021祥云杯】 鸣雏恋