D365 FO 科目维度余额计算

D365 FO提供了一系列的类用来做维度余额的计算。
凭证的数据存在GeneralJournalAccountEntry和GeneralJournaEntry表里,维度值是表GeneralJournalAccountEntry的LedgerDimension.
LedgerDimension对应的值在表DimensionAttributeValueCombination里,而具体维度的值又在不同的表里。
有一些维度组合值是实际业务在生成凭证的时候就使用了,而有一些维度组合是为了分析时用的,比如实际业务发生的过程中,部门,成本中心和利润中心都录入了
所以凭证GeneralJournalAccountEntry里存的是部门,成本中心和利润中心的组合值,但是需要如果需要按照成本中心和利润中心的组合进行分析,这个值在凭证表里并没有保存。
为了实现维度组合的计算,AX2012用了财务维度集的概念,定时把GeneralJournalAccountEntry的数据按照中某个维度组合的数据写入到DimensionFocusBalance表里,方便查询任意组合财务维度的值。

总账->会计科目表->维度->财务维度集

 对于新建的财务维度集要重建余额,也要定期更新余额,要不然最新的凭证记录不在DimensionFocusBalance里,调用系统的类计算维度余额也就查询不到正确的结果了。
1.使用LedgerBalanceBase查询
根据某个维度值的组合,查找某个期间的发生额,可以用如下类。

 这里以LedgerBalanceDimAttrValueComboAmounts类为例,查询某个科目在某个利润和成本中心组合下的期间发生额。

1 LedgerBalanceDimAttrValueComboAmounts       ledgerBalanceDimAttrValueComboAmounts = LedgerBalanceDimAttrValueComboAmounts::construct();
2         RecId recId = DimensionDynamicAccountResolver::newResolver("51010102-001--007-010").resolve();
3 
4         ledgerBalanceDimAttrValueComboAmounts.parmAccountingDateRange(dateStartMth(systemDateGet()), dateEndMth(systemDateGet()));        
5         ledgerBalanceDimAttrValueComboAmounts.parmIncludeRegularPeriod(true);
6         ledgerBalanceDimAttrValueComboAmounts.calculateBalance(DimensionAttributeValueCombination::find(recId));
7         info (num2Str(ledgerBalanceDimAttrValueComboAmounts.getAccountingCurrencyBalance(), 0, 2, 0 ,0));

如果想查询某个具体的维度组合的值对应的期间发生额,可以用这些类进行查询。
2.使用试算平衡表
如果想一次性获取某个财务维度集里包含的所有维度组合的值,通过方法一就显得笨拙了,不可能一个个维度组合起来再一个个查询出来。
参考试算平衡表的做法。
路径:总账->查询和报表->试算平衡表
如果要计算财务维度集ProfitCost某个期间段的金额情况,可以用如下代码:

 1 private LedgerTrialBalanceTmp getMainAccountBalances(
 2                 MainAccountNum       _mainAccountId,
 3                 StartDate           _startDate,
 4                 EndDate             _endDate,
 5                 Name                _dimensionHierarchyName)
 6     {
 7         ttsbegin;
 8 
 9         //更新财务维度集余额
10         DimensionFocusUpdateBalance::updateBalance(
11             DimensionHierarchy::findByTypeAndName(DimensionHierarchyType::Focus, 
12             _dimensionHierarchyName));
13 
14         LedgerTrialBalanceTmp trialbalanceTmp;                                                                        
15         delete_from trialBalanceTmp;
16 
17         LedgerTrialBalanceContract trialBalanceContract = new LedgerTrialBalanceContract();
18         trialBalanceContract.parmFromDate(_startDate);
19         trialBalanceContract.parmToDate(_endDate);
20         trialBalanceContract.parmIncludeOpening(true);
21         trialBalanceContract.parmIncludeClosingAdjustments(false);
22         trialBalanceContract.parmIncludeClosingTransactions(false);
23 
24         //设置过账层
25         List list = new List(Types::Integer);        
26         list.addEnd(0);
27         trialBalanceContract.parmPostingLayers(list);                
28         trialBalanceContract.parmPrimaryDimensionFocus(_dimensionHierarchyName);
29         //设置过滤科目        
30         Map map = new Map(Types::Int64, Types::String);            
31         map.insert(DimensionAttribute::findByName("MainAccount").RecId, _mainAccountId);
32         trialBalanceContract.parmDimensionRangeMap(map);
33         
34         LedgerTrialBalanceDP trialBalanceDP = new LedgerTrialBalanceDP();
35         trialBalanceDP.parmDataContract(trialBalanceContract);
36         trialBalanceDP.setTrialBalanceTmpTable(trialBalanceTmp);
37 
38         trialBalanceDP.processReport();
39         ttscommit;
40         return trialbalanceTmp;
41     }

调用示例

 1  private void callMainAccountBalances()
 2     {
 3         LedgerTrialBalanceTmp trialbalanceTmp = this.getMainAccountBalances(
 4                                 "51010406",
 5                                 dateStartMth(systemDateGet()),
 6                                 dateEndMth(systemDateGet()),
 7                                 "ProfitCost");                                       
 8         while select trialbalanceTmp
 9         {
10             info(trialbalanceTmp.DimensionValues[1] + " " +
11                 trialbalanceTmp.DimensionValues[2] + " " +
12                 trialbalanceTmp.DimensionValues[3] + " " +
13                 num2Str(trialbalanceTmp.EndingBalance, 0,2, 0 ,0) + 
14                 num2Str(trialbalanceTmp.AmountDebit, 0,2, 0, 0));
15         }
16     }

D365 FO,目前版本10.0.0.10根据某个维度过滤的代码,感觉有点问题,不知道是微软特意这么设计还是bug。
比如想按照某个科目过滤,可以用Map把科目传进LedgerTrialBalanceContract,代码在执行的时候是通过表LedgerTransAccountTmp的fillFromDimSetBalWithDimRanges方法取得要过滤的LedgerDimension集合。
问题在代码的第215行

r.value(SysQuery::range(_startDate, _endDate));

这行代码是按照调用方的开始和结束日期来过滤的,问题在于,试算平衡表是要看期初金额的,这样过滤的话,如果调用方输入的期间内,map里指定的科目维度没有发生交易,期初都出不来了。
不清楚微软fillFromDimSetBalWithDimRanges这个方法设计的初衷,作为调用方来说,我的期望是,如果没传Map进行过滤的时候能返回某个科目维度的记录,那么传了这个科目维度进行过滤的话,也应该能出来。
不能因为过滤了,就导致不出现这条记录了。
修改也就简单,扩展一下这个方法,把逻辑改的跟不过滤的逻辑保持一致,把_startDate改成periodStartDate 
date periodStartDate = LedgerFiscalCalendar::findOpeningStartDateByDate(Ledger::fiscalCalendar(CompanyInfo::current()), _startDate);

原文地址:https://www.cnblogs.com/Farseer1215/p/12872838.html