单据模板元素

单据模板元素是对单据模板API的二次封装,提供比底层API更加符合使用习惯的函数。

对于已经存在于系统中的单据模板元素,可以对其数据进行增删查和过账操作。

实例化单据模板元素

例如系统中存在 Journal_elimadj 单据模板:

b5a0db68e42e4fdd8c0e0f35c0320dd3

可以通过提供元素名 Journal_elimadj 初始化,若元素名不唯一,增加提供path信息即可

[1]:
import pandas as pd
from deepfos.element.journal_template import *

jt = JournalTemplate('Journal_elimadj')

常用信息

由于单据模板涉及的组件较多,元信息也比较繁杂,在该元素类中提供了若干cached_property,用以访问相应的依赖组件元素对象及数据表类对象或提供有针对性的信息。

例如 basic_info 内为概览式的组件使用信息和元素信息:

[2]:
jt.basic_info.dict()
[2]:
{'approveRecord': {'id': 'DAT_MYSQL202208080912433008317',
  'dbName': 'Journal_elimadj_process',
  'folderId': 'DIR84a79ff4a875',
  'dbPath': '\\journal_template\\08_Journal\\03_Elim\\',
  'actualTableName': 'ehcdge006_limadj_process_b7xef',
  'serverName': 'data-table-mysql-server1-0',
  'description': {'zh-tw': '審批記錄表',
   'en': 'approval record table',
   'zh-cn': '审批记录表'},
  'oldInfo': None},
 'closeAccountDB': {'id': 'DAT_MYSQL202208080912431763706',
  'dbName': 'Journal_elimadj_status',
  'folderId': 'DIR84a79ff4a875',
  'dbPath': '\\journal_template\\08_Journal\\03_Elim\\',
  'actualTableName': 'ehcdge006_elimadj_status_9g0ed',
  'serverName': 'data-table-mysql-server1-0',
  'description': {'zh-tw': '數據狀態表',
   'en': 'Data status table',
   'zh-cn': '数据状态表'},
  'oldInfo': None},
 'folderId': 'DIR84a79ff4a875',
 'path': '\\journal_template\\08_Journal\\03_Elim\\',
 'templateId': 'JT5jj35mgnvkmc',
 'code': 'Journal_elimadj',
 'description': {'en': 'Journal_elimadj', 'zh-cn': '抵销调整日记账'},
 'rsElementDetail': None,
 'styles': {},
 'defaultItem': 10}

close_account 为关账流程数据表的元素对象:

[3]:
jt.close_account, jt.close_account.table_name
[3]:
(<deepfos.element.datatable.APIBasedDataTableMySQL at 0x1c7f99bddf0>,
 'ehcdge006_elimadj_status_9g0ed')

approval_record 为审批记录数据表的元素对象:

[4]:
jt.approval_record, jt.approval_record.table_name
[4]:
(<deepfos.element.datatable.APIBasedDataTableMySQL at 0x1c7d708ca60>,
 'ehcdge006_limadj_process_b7xef')

header_columns 为日记账头字段列表:

[5]:
jt.header_columns
[5]:
['journal_name',
 'journal_code',
 'Scenario',
 'year',
 'period',
 'entity',
 'Group',
 'remark',
 'data_status',
 'posting_status',
 'invalid_status']

header_columns 为日记账体字段列表:

[6]:
jt.body_columns
[6]:
['ICP',
 'account',
 'currency',
 'rate',
 'original_debit',
 'original_credit',
 'debit',
 'credit',
 'amount',
 'C1',
 'C2',
 'C3',
 'note']

field_type 为字段类型信息:

[7]:
jt.field_type
[7]:
{'journal_name': (1, 1),
 'journal_code': (1, 15),
 'Scenario': (1, 8),
 'year': (1, 8),
 'period': (1, 8),
 'entity': (1, 8),
 'Group': (1, 8),
 'remark': (1, 1),
 'data_status': (1, 1),
 'posting_status': (1, 1),
 'invalid_status': (1, 1),
 'ICP': (2, 8),
 'account': (2, 8),
 'currency': (2, 8),
 'rate': (2, 15),
 'original_debit': (2, 15),
 'original_credit': (2, 15),
 'debit': (2, 15),
 'credit': (2, 15),
 'amount': (2, 15),
 'C1': (2, 8),
 'C2': (2, 8),
 'C3': (2, 8),
 'note': (2, 1)}

posting_info 为过账配置信息:

[8]:
{col.modelInfo.code: col.modelInfo.description for col in jt.posting_info.dimensionModel}
[8]:
{'FinanciaIM_basic': {'zh-cn': '主模型'}, 'FinancialM_JE': {'zh-cn': '日记账模型'}}

posting_params 为过账参数信息:

[9]:
jt.posting_params
[9]:
{'Common_parameters': [{'dimensionInfo': {'elementName': 'Scenario',
    'elementType': 'DIM',
    'folderId': 'DIR60331debe5b5',
    'path': '\\journal_template\\01_Dimension\\',
    'serverName': 'dimension-server1-2'},
   'dimensionCode': 'Scenario',
   'relationColumn': {'code': 'Scenario',
    'type': 'M',
    'description': {'en': 'Scenario', 'zh-cn': '情景'},
    'showName': 'Scenario'},
   'dimensionUsage': 1,
   'displayName': 'Scenario',
   'valueKey': 'Scenario{Descendant(TotalScenario,0)}',
   'singleKey': 'FinanciaIM_basicScenarioScenarioDIMDIR60331debe5b51Scenario'},
  {'dimensionInfo': {'elementName': 'Year',
    'elementType': 'DIM',
    'folderId': 'DIR60331debe5b5',
    'path': '\\journal_template\\01_Dimension\\',
    'serverName': 'dimension-server1-2'},
   'dimensionCode': 'Year',
   'relationColumn': {'code': 'year',
    'type': 'M',
    'description': {'en': 'Year', 'zh-cn': '年度'},
    'showName': 'year'},
   'dimensionUsage': 5,
   'displayName': 'Year',
   'valueKey': 'Year{Descendant(Total_Year,0)}',
   'singleKey': 'FinanciaIM_basicyearYearDIMDIR60331debe5b55Year'},
  {'dimensionInfo': {'elementName': 'Period',
    'elementType': 'DIM',
    'folderId': 'DIR60331debe5b5',
    'path': '\\journal_template\\01_Dimension\\',
    'serverName': 'dimension-server1-2'},
   'dimensionCode': 'Period',
   'relationColumn': {'code': 'period',
    'type': 'M',
    'description': {'en': 'Period', 'zh-cn': '期间'},
    'showName': 'period'},
   'dimensionUsage': 6,
   'displayName': 'Period',
   'valueKey': 'Period{Base(TotalPeriod,0)}',
   'singleKey': 'FinanciaIM_basicperiodPeriodDIMDIR60331debe5b56Period'},
  {'dimensionInfo': {'elementName': 'Entity',
    'elementType': 'DIM',
    'folderId': 'DIR60331debe5b5',
    'path': '\\journal_template\\01_Dimension\\',
    'serverName': 'dimension-server1-2'},
   'dimensionCode': 'Entity',
   'relationColumn': {'code': 'entity',
    'type': 'M',
    'description': {'en': 'Entity', 'zh-cn': '实体'},
    'showName': 'entity'},
   'dimensionUsage': 3,
   'displayName': 'Entity',
   'valueKey': 'Entity{Descendant(XTCHB,0);Descendant(DJHB,0)}',
   'singleKey': 'FinanciaIM_basicentityEntityDIMDIR60331debe5b53Entity'}],
 'FinancialM_JE': [],
 'FinanciaIM_basic': []}

查询单据数据

现有的单据数据查询接口需要提供详尽的查询字段、筛选字段及排序字段信息,且其中的类型代码本身需从元信息中获得,因此在本元素类中提供了 new_query 成员方法和 table property,分别用于代表查询条件的组织体和其中的表,并可从 table 中通过访问成员变量的方式直接表示表中的字段,且当字段用于表示筛选条件时,可以在字段名后通过访问成员方法的方式加入条件的组成部分,描述为完整的筛选条件。

具体用法见下:

首先,从单据模板元素的实例中获得 table property:

[10]:
t = jt.table
t
[10]:
<deepfos.element.journal_template.Table at 0x1c7f9b2b3a0>

使用 new_query 方法新建一个查询条件的组织体

[11]:
q = jt.new_query()
q
[11]:
<deepfos.element.journal_template.QueryBuilder at 0x1c7f9b2b790>

通过在组织体q中访问其 columns , whereorder_by 方法,传入相应的条件和字段信息。

如下组织的内容为:

查询 ICP 值等于”HC”且 period 大于”2”的 ICP , entity , period , posting_statusoriginal_credit 字段的数据,以 ICP 字段升序且 period 字段降序的方式排序

[12]:
data = jt.batch_query(
        q.columns([t.ICP, t.entity, t.period, t.posting_status, t.original_credit])
         .where([
                    t.ICP.eq("HC"),
                    t.period.gt("2")
                ])
         .order_by([t.ICP, t.period.desc()])
)
data
[12]:
journal_code ICP entity period posting_status
0 4 HC JH 4 N

目前table内字段支持的函数:

方法名

意义

is_in

在列表中

not_in

不在列表中

eq

等于

ne

不等于

contains

包含

gt

大于或晚于

gte

大于等于

lt

小于或早于

lte

小于等于

latter_than

大于或晚于

earlier_than

小于或早于

asc

升序

desc

降序

亦可不提供 columns 信息,此时查询到的是包括符合条件的所有非全空列数据

[13]:
q = jt.new_query()
data = jt.batch_query(
        q.where([
                    t.ICP.eq("HC"),
                    t.period.gt("2")
                ])
         .order_by([t.ICP, t.period.desc()])
)
data
[13]:
journal_code journal_name Scenario year period entity Group remark data_status posting_status invalid_status ICP account rate original_debit debit amount C2 C3
0 4 adada Actual 2018 4 JH A1_Audit aaaaa N N N HC ownership 3.0 74111.0 222333.0 222333.0 None None

注意事项:

每次组织新的查询结构体信息,都需要通过 new_query 方法获得新的 QueryBuilder ,否则组织的结构体信息可能包含了之前使用时定义的内容。

通过 batch_query 方法查到的结果是DataFrame格式的,如需原始返回数据,可用 batch_query_raw 方法

[14]:
q = jt.new_query()
data = jt.batch_query_raw(
        q.where([
                    t.ICP.eq("HC"),
                    t.period.gt("2")
                ])
         .order_by([t.ICP, t.period.desc()])
)
data
[14]:
[{'journal_code': 4,
  'journal_name': 'adada',
  'Scenario': 'Actual',
  'year': '2018',
  'period': '4',
  'entity': 'JH',
  'Group': 'A1_Audit',
  'remark': 'aaaaa',
  'data_status': 'N',
  'posting_status': 'N',
  'invalid_status': 'N',
  'ICP': 'HC',
  'account': 'ownership',
  'rate': 3.0,
  'original_debit': 74111.0,
  'debit': 222333.0,
  'amount': 222333.0,
  'C2': 'None',
  'C3': 'None'}]

默认情况下,查询时的可选参数 show_detailFalse , 表示不包括其下明细,如需包括,则可设为 True

[15]:
q = jt.new_query()
data = jt.batch_query(
        q.columns(["ICP", "entity","period", "posting_status", "original_credit"])
         .where([
                    t.ICP.eq("HC"),
                    t.period.gt("2")
                ])
         .order_by([t.ICP, t.period.desc()]),
    show_detail=True
)
data
[15]:
journal_code ICP entity period posting_status
0 4 HC JH 4 N

批量保存单据数据

单据模板的数据保存的接口需要自己组织日记账头和日记账体的关联关系,且需包括一些元素信息,因此在元素类中将这个操作进行了简化,可以通过提供带有index的日记账头和日记账体的DataFrame数据,或者两者间可作为关联数据的列信息,在方法内会以关联关系组织出相应的请求

例如有如下的日记账头和日记账体数据,需要以日记账体的 rate 字段为单个保存体的分批依据,若在日记账头的数据里加入了 helper_rate 字段用于标识头和体的关联信息

[16]:
main = pd.DataFrame([
    {"journal_name": "foo", "year": "2022", "period": "1", "entity": "JH", "helper_rate": 1},
    {"journal_name": "bar", "year": "2021", "period": "2", "entity": "JH", "helper_rate": 2}
])

body = pd.DataFrame([
         {"ICP": "HC", "account": "101", "rate": 1, "original_credit": 222, "credit": 222},
         {"ICP": "HCX", "account": "o1", "rate": 2, "original_credit": 111, "credit": 111},
         {"ICP": "HC", "account": "102", "rate": 1, "original_credit": 222, "credit": 222},
         {"ICP": "HCX", "account": "o2", "rate": 2, "original_credit": 111, "credit": 111}
     ]
)
[17]:
main
[17]:
journal_name year period entity helper_rate
0 foo 2022 1 JH 1
1 bar 2021 2 JH 2
[18]:
body
[18]:
ICP account rate original_credit credit
0 HC 101 1 222 222
1 HCX o1 2 111 111
2 HC 102 1 222 222
3 HCX o2 2 111 111

则可在传入 batch_save 方法前,通过分别设定日记账头和日记账体的index的方式准备关联关系,再传入 batch_save 方法

[19]:
main = main.set_index(['helper_rate'], drop=False)
body = body.set_index(['rate'], drop=False)
[20]:
journal_codes = jt.batch_save(main, body)
journal_codes
[20]:
['123', '124']

通过返回和请求日志可看出,实际存了两批数据,且对应关系无误

对于日记账头和日记账体存在关联列为相同列名时,亦可直接在 batch_save 的入参中提供列信息,将以该列信息为日记账头和日记账体的index,做后续关联

[21]:
main = pd.DataFrame([
    {"journal_name": "foo", "year": "2022", "period": "1", "entity": "JH", "rate": 1},
    {"journal_name": "bar", "year": "2021", "period": "2", "entity": "JH", "rate": 2}
])

body = pd.DataFrame([
        {"ICP": "HC", "account": "101", "rate": 1, "original_credit": 222, "credit": 222},
        {"ICP": "HCX", "account": "o1", "rate": 2, "original_credit": 111, "credit": 111},
        {"ICP": "HC", "account": "102", "rate": 1, "original_credit": 222, "credit": 222},
        {"ICP": "HCX", "account": "o2", "rate": 2, "original_credit": 111, "credit": 111}
    ]
)

jt.batch_save(main, body, columns=['rate'])
[21]:
['125', '126']

说明:

对于保存数据较大的,可使用 chunksize 参数来限制单次批量保存的数据大小, chunksize 以单行日记账头为单位,默认为1000

删除单据数据

本元素类中提供的删除方法接受两种入参,一种为字段和筛选条件值的列表组成的字典,另一种为可转换为前者的DataFrame,可以适用于不同的使用场景。

已知单据数据编号时的删除入参:

[22]:
jt.delete({"journal_code": journal_codes})
[22]:
DataTableCustomSqlResultDTO(modifyCounts=6, operation='delete', selectResult=None)

说明:

此处的modifyCounts包括了日记账头表的2行和对应子表里的4行。

用已知筛选条件的删除入参:

[23]:
jt.delete({"year": ["2022"], "period": ["1"]})
[23]:
DataTableCustomSqlResultDTO(modifyCounts=3, operation='delete', selectResult=None)

说明:

此处的modifyCounts包括了日记账头表的1行和对应子表里的2行。

先通过较少的条件筛选出符合条件的数据,再用得到的DataFrame数据作为筛选条件删除:

[24]:
q = jt.new_query()
data = jt.batch_query(
        q.where([
                    t.ICP.is_in(["HC", "HCX"]),
                    t.year.is_in(["2021"])
                ])
         .order_by([t.ICP, t.period.desc()]),
    show_detail=True
)
data = data[set(jt.header_columns).intersection(data.columns)]
data
[24]:
invalid_status data_status journal_name journal_code period entity posting_status year
0 N N bar 126 2 JH N 2021
1 N N bar 126 2 JH N 2021

说明:

在提供筛选条件时,筛选的字段需属于日记账头表字段,因此需要增加列的交集处理。

[25]:
jt.delete(data)
[25]:
DataTableCustomSqlResultDTO(modifyCounts=3, operation='delete', selectResult=None)

说明:

此处的modifyCounts包括了日记账头表的1行和对应子表里的2行。

过账操作相关

单据模板的过账和取消过账接口接受的入参字段是一致的,所以本元素类提供的方法入参也相同。

可以接受两种入参,一种是接受维度字段和筛选值的列表组成的字典,在当前单据模板元素的过账类别只有一种时,作为该类别的筛选条件,否则将试图以该筛选条件作为过账和取消过账时对应前端的“共有参数”区域的筛选条件组织请求,“共有参数”的前端界面可参考如下:

0097ae4b21e74213858506d29c8ca57e

另一种是在业务需要的情况下,需要提供过账到不同内存财务模型中的特有参数,此时如果需要定制全量的参数类别与筛选条件的信息,则应通过 FullPostingParameter 类,具体可见更复杂的过账信息的处理方法

过账

在过账参数类别只有一种或只需过账至共有参数时,可以只提供该类别下的过账的维度筛选信息:

[26]:
jt.get_posting({'Year': ['2022'], 'Period': ['1'], 'Entity': ['JLU'], 'Scenario': ['Actual']})
[26]:
{'messageList': [{'status': True, 'code': 0, 'message': '过账成功0条'}]}

取消过账

与过账类似,在取消过账参数类别只有一种或只需过账至共有参数时,可以只提供该类别下的取消过账的维度筛选信息:

[27]:
jt.cancel_posting({'Year': ['2022'], 'Period': ['1'], 'Entity': ['JLU'], 'Scenario': ['Actual']})
[27]:
{'messageList': [{'status': True, 'code': 0, 'message': '取消过账成功0条'}]}

更复杂的过账信息的处理方法

在过账配置中有不止一个过账参数类别,且需补充所提供的筛选条件属于哪个类别的信息时,需要组织详细的过账信息。

可以先通过 posting_params 属性查看现有的类型:

[28]:
list(jt.posting_params.keys())
[28]:
['Common_parameters', 'FinancialM_JE', 'FinanciaIM_basic']

例如需要补充组织附加的内存财务模型 FinancialM_JE 这个参数类别的过账信息,则可在初始化 FullPostingParameter 的时候,传入成员为 FinancialM_JECommon_parameters 的列表,意为包含的类别是FinancialM_JE和Common_parameters

[29]:
full_params = FullPostingParameter(['FinancialM_JE', 'Common_parameters'])

然后就可以通过访问 Common_parametersFinancialM_JE 成员的方式设置相应过账参数

[30]:
full_params.Common_parameters={'Year': ['2022'], 'Period': ['11', '12'], 'Entity': ['1022'], 'Scenario': ['Actual']}
[31]:
full_params.FinancialM_JE={'cube': "aaa"}

最后调用 get_posting 方法

[32]:
jt.get_posting(full_params)

需注意的是,只有 FullPostingParameter 初始化时传入的参数类别是可以设置的,其他值都会被禁止:

[33]:
full_params.FinancialM_JEs={'cube': "aaa"}
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [33], in <cell line: 1>()
----> 1 full_params.FinancialM_JEs={'cube': "aaa"}

File D:\ZHUWJ\projects\deepfos\deepfos\element\journal_template.py:84, in FullPostingParameter.__setattr__(self, name, value)
     82 else:
     83     if name not in self._all_categories:
---> 84         raise ValueError(f'成员: {name} 未设置为已知参数类别,'
     85                          f'已有参数类别:\n{set(self._all_categories.keys())}')
     86     self._all_categories[name] = value

ValueError: 成员: FinancialM_JEs 未设置为已知参数类别,已有参数类别:
{'FinancialM_JE', 'Common_parameters'}

注意事项:

每次组织全量过账筛选信息,都需要重新实例化一个新的 FullPostingParameter 对象,否则组织的全量过账筛选信息可能包含了之前使用时设置的内容。