月度日期表是颗粒度为月的日期表,主要用来解决会计领域特有的第13个月问题,就实用性来讲价值不大,但却十分适合用来练手,所以和各位分享一下。日期表的制作非常像切蛋糕的过程,所以十分适合通过参数表来构造。以7月为第一个财政月为例,月度日期表有两种情况:

第一种情况是总共有13个财政月,但是普通的月份(Month_Number)仍然只有12个,此时要求第12个财政月和第13个财政月对应的普通月份相同。

第二种情况是总共有13个财政月和13个普通月份,此时要求第13个财政月与第13个普通月份相同。因为存在这两种情况,所以需要Month_For_Key这个列作为桥梁负责连接月度日期表和事实表(Fact Table)。类似于标准日期表,接下将讲解各个参数(表)。

第一个要介绍的参数是First_Fiscal_Month,这个参数为1到12中的其中一个数,用来控制第一个财政月。

第二个要介绍的参数为Months_In_Year,这个参数只能为12或者13用来控制每年的最大财政月, 其他的值似乎没有实际含义。

第三个要介绍的参数是NoOfCalM,用来控制Month_Number中的最大值,这个参数只允许是12或者13,其他的值目前没有想到实际的含义。

第四个要介绍的参数是First_Date,这个值为事实表中最早的日期。

第五个要介绍的参数是Last_Date,这个值为事实表中最大的日期。

let
Constant_List = {1..12},
Upper_Part =
List.LastN(
Constant_List,
each _ ˃= First_Fiscal_Month
),
Lower_Part =
List.FirstN(
Constant_List,
each _ ˂ First_Fiscal_Month
),
Month_No_List_BAU =
List.Combine(
{
Upper_Part,
Lower_Part,
List.Numbers(
13,
Months_In_Year - 12
)
}
),
Month_No_List =
if
NoOfCalM = 12
then
List.Alternate(
List.Repeat(
List.Combine(
{
Upper_Part,
Lower_Part
}
),
Months_In_Year - 12 + 1
),
11,
1,
12
)
else
Month_No_List_BAU,
To_Table =
Table.FromColumns(
{
{1..Months_In_Year},
Month_No_List,
Month_No_List_BAU
},
type table
[
Fiscal_Month_Number = Int64.Type,
Month_Number = Int64.Type,
Month_For_Key = Int64.Type
]
),
Add_Offset_For_Year_Number =
Table.AddColumn(
To_Table,
"Offset_For_Year_Number",
each if
[Month_Number] ˃ [Fiscal_Month_Number]
then
-1
else
0,
Int64.Type
)
in
Add_Offset_For_Year_Number
第六个要介绍的参数为Full_M_Mapping。对应代码的大意为:(1)利用List.FirstN()和List.LastN()调整1到12月的相对位置;(2)如果是总共有13个财政月,但是普通的月份(Month_Number)仍然只有12个的情形,需要在(1)的结尾重复一次最后一个元素,另一种情形就是需要补上13;(3)不难发现Month_For_Key就是(2)的第二种情形的结果;(4)使用Table.FromColumns()把之前(1),(2)和(3)组合成的复合List转化为表格。

第七个要介绍的参数为Month_Quarter,用来辅助计算财政季度和普通的季度。

第八个要介绍的参数为Full_Mapping,构造过程大致可以描述为:FM_M_Mapping内嵌于Month_Quarter两次,分别求财政季度和普通季度,以及每个月在当前季度中是第几个月,然后添加必要的计算列。

let
Month_No_For_First_Date = Date.Month(First_Date),
Year_No_For_First_Date = Date.Year(First_Date),
Month_No_For_Last_Date = Date.Month(Last_Date),
Year_No_For_Last_Date = Date.Year(Last_Date),
Mapping_For_First_Month =
Table.SelectRows(
Full_Mapping,
each [Month_Number] = Month_No_For_First_Date
),
Mapping_For_Last_Month =
Table.SelectRows(
Full_Mapping,
each [Month_Number] = Month_No_For_Last_Date
),
Cal_First_Fiscal_Month =
List.Single(
Table.Column(
Mapping_For_First_Month,
"Fiscal_Month_Number"
)
),
Cal_First_Fiscal_Year =
Value.Subtract(
Year_No_For_First_Date,
List.Single(
Table.Column(
Mapping_For_First_Month,
"Offset_For_Year_Number"
)
)
),
Cal_Last_Fiscal_Month =
List.Single(
Table.Column(
Mapping_For_Last_Month,
"Fiscal_Month_Number"
)
),
Cal_Last_Fiscal_Year =
Value.Subtract(
Year_No_For_Last_Date,
List.Single(
Table.Column(
Mapping_For_Last_Month,
"Offset_For_Year_Number"
)
)
),
First_Fiscal_Year_Month_Number =
Value.Add(
Cal_First_Fiscal_Month,
Value.Multiply(
Cal_First_Fiscal_Year,
Months_In_Year
)
),
Last_Fiscal_Year_Month_Number =
Value.Add(
Cal_Last_Fiscal_Month,
Value.Multiply(
Cal_Last_Fiscal_Year,
Months_In_Year
)
),
All_Fiscal_Year_Month_Number =
List.Numbers(
First_Fiscal_Year_Month_Number,
Value.Add(
Value.Subtract(
Last_Fiscal_Year_Month_Number,
First_Fiscal_Year_Month_Number
),
1
)
),
Iteration =
List.Transform(
All_Fiscal_Year_Month_Number,
each
let
Repeated_Cal = _ - 1,
Result =
{
_,
Value.Add(
Number.Mod(
Repeated_Cal,
Months_In_Year
),
1
),
Number.IntegerDivide(
Repeated_Cal,
Months_In_Year
)
}
in
Result
),
To_Table =
Table.FromRows(
Iteration,
type table
[
Fiscal_Year_Month_Number = Int64.Type,
Fiscal_Month_Number = Int64.Type,
Fiscal_Year_Number = Int64.Type
]
)
in
To_Table
这是最后的参数表Fiscal_Year_Number, 对应代码的大意:首先根据4*财政年 + 财政月分别计算出First_Date和Last_Date的Fiscal_Year_Number,然后构建以这两个数为首尾的数列,最后把这些数分别代入Mod和Quotient求对应的财政月和财政年。

最后,把Full_Mapping和Fiscal_Month_Number拼接起来就是月度日期表。
真的长