关系数据库中的重复记帐条目

译者的话 :在尼日利亚金融科技公司工作期间,我不得不从头开始创建一个付款系统。 那时,我真的对会计一无所知,如何更好地存储付款和余额。 但是有人怀疑,用户帐户中带有一个余额数字的原始期权太简单而无法正确。


本文帮助我理解并避免了此事。 同时,关于“如何建立自己的支付系统”主题的信息非常少,程序员很难理解会计方面的书籍(而且非常乏味)。 我希望本文对那些打算做这样的事情的人有用。


我马上对俄语语言的财务术语可能会出现的错误表示歉意-我仍然是一名程序员,而不是一名会计师,并且我对该领域的俄语术语不熟悉。


引言


许多使用关系数据库的计算机系统都存储有关余额和交易的某种财务信息。 而且,这种数据库的设计和开发经常引起如何存储该信息的问题。 通常,选择是在便宜的“简单记录”和更复杂的“双重记录”之间进行的。



卢卡·帕乔利(Luca Pacioli),是一本幸存下来的最古老的书籍(描述15世纪),


在“简单记录”系统中,数值仅记录一次。 在两次录入系统中,每个值两次记录为贷方(正值)和借方(负值)。 有一组规则确定这些值之间的关系。 尽管有经验的会计师甚至都无法想象如何在关系数据库中表示这些规则,但它们很容易为您描述。


基本规则如下:


  1. 系统中的每个条目都必须保持平衡,即 一个操作中所有值的总和应为零。
  2. 整个系统在任何给定时间的所有值之和应为零(所谓的“试验平衡”规则)。
  3. 已经输入数据库的值无法编辑或删除。 如果需要校正,则必须先用另一个带有相反符号的操作取消该操作,然后以正确的值重复该操作。 这使您可以实施可靠的审核跟踪(所有交易的完整日志,通常在检查过程中需要)。

双重簿记适用性


在项目开始时,简单记录的低价格总是诱人的,实现成本和全面的双重记录的复杂性似乎是不必要的。 但是,实际上,经常使用简单的记录是一种错误的节省。


如果IT系统中的会计信息仅复制正在开发的数据库外部存储的现有纸质记录,则简单记录仍然具有生命权。 但是,如果有关以下系统的至少一项事实是正确的,则应从一开始就使用两次输入:


  1. 如果需要对信息进行会计审计
  2. 如果系统中的信息是有关该属性的唯一信息源
  3. 如果信息涉及高价值的物品
  4. 如果计划将来认真开发该系统

两次输入示例


重复输入的关键思想是存在一个特殊的“现金簿”帐户( 大约是Transl 。:我没有找到如何用俄语充分称呼它,有人可以告诉我吗? )。 该帐户包含贵重物品(例如金钱)从我们的会计系统中存放或提取时的记录。 因此,该帐户的当前余额反映了系统中值的总数。


下面是一个简单的示例,其中包含两个帐户“现金簿”和“史密斯”。


(a)将300英镑输入系统并存入Smith的帐户。 在史密斯的帐户中创建了300英镑的贷款(右侧为贷方,左侧为借方)。 为了使该金额平均,在现金簿账户中创建了300英镑的借方。



(b)然后史密斯从系统中扣除50英镑。 我们在史密斯的帐户中为此金额创建借方,在现金簿中创建贷方。



(c)添加另一个Pattel帐户,并从Smith那里转账100英镑给他。 为此,我们需要为此与Smith借记,并与Pattel借贷。


(d)最后,让Pattel从系统中提取60英镑。 我们在他的帐户中创建借方,在现金簿中创建贷方。



所有这些操作的结果是,我们可以计算出Smith的总余额为150英镑,Patterla的总余额为40英镑,在Cash Book中的总余额为-190英镑,所有其他帐户的余额均为负数。 根据将来的这些简单规则和操作,您可以构建一个非常全面的价值控制系统。


资料模型


可用于表示所有这些信息的简单数据模型的结构:



POSTING表包含重复项本身。 将所有数字存储在一张表中可大大简化所有计算。 单调递增的计数器应用作主键。 在这种情况下,这些值应按行连续显示,您始终可以确保未删除任何记录。 BATCH和journal表用于控制数据并将数据输入到该POSTING表中。


在Journal表中的每个条目代表一个事务(从业务角度来看),该事务生成重复条目。 这样的交易是工作或业务流程的完整单元。 与Journal记录关联的所有POSTING记录必须成功完成,或者都不成功。 单个事务中所有POSTING记录的总和应为零。 上例中的每个传输操作均以它在Journal表中的条目表示


在BATCH表中进行输入是为了方便数据输入。 它用于将Journal记录分组为方便的程序包,例如,用于进入系统的一组检查,某种全局业务流程,例如一次向所有用户收取利息等。


ACCOUNT表存储有关系统中值所有者的数据。


ASSET TYPE表包含有关系统中使用的值的类型的信息。 通过将值类型添加到POSTING表的主键中,可以使系统一次处理多种类型的值(例如,处理多种货币)。


以下是这种数据库以最简化的形式查找上述示例的方式:



在完成来自Journal的任何事务后,POSTING表中Amount列的余额始终为零(软件应确保数据库中没有未完成事务的记录)。


现金簿帐户的总和为-190,等于具有相反符号的Smith和Pattel的余​​额之和。


为了演示多币种操作,添加了一种新的价值。 如果Smith希望以1兑换1.5的比率将20磅换成美元,则交易将通过“现金簿”以这种方式进行:



帐单期


我们得到的模型看起来不错,但实际上,由于我们无法删除任何东西,并且被迫不断地重新计算POSTING中越来越多的记录,因此在高负载下它会很快崩溃。


大多数会计系统都有计费期的概念,通常是一个月,三个月或一年。 这样的时间段提示了划分数据流的便利点。 通常方便的一点是年底,日历或财务。


我们可以在POSTING表及其主键中添加带有句号指示符的列,将数据分成可独立处理的组。 如果在上面的示例中,某些记录属于新的结算期,则帐户余额将按以下方式结转。


首先,将清除上一期间的余额。


图片


然后将它们转移到新时期



一定时间后,可以将YEAR 1期间的所有记录发送到归档文件并从系统中删除,而不会失去其完整性。


交易汇总


会计系统中的某些操作可能会同时影响许多甚至所有用户。 例如,以所有用户当前余额的形式向所有用户支付利息。


此类操作可以作为Journal表中的单个事务的一部分进行处理,并且您可以将使用Cash Book的所有操作聚合到POSTING表中的一个公用记录中(而不是为每个帐户创建单独的操作)。 这将使您能够遵守上述所有会计规则,同时将数据库中的记录数减半。 使用这种方法,数据库中的年末将如下所示:



批处理


批处理通常用于简化数据输入会计系统的过程。


从历史上看,支票处理就是这样工作的。 给会计师一包十张支票,一包的数目和所有支票的总额。 在第一阶段,检查以“未经授权”条目的形式输入系统。 在这种情况下,将通过BATCH表检查其数量和总量,只有允许用户匹配正确的值,才允许用户提交数据包。 完成此操作后,将包裹发送给另一名员工,由其检查其有效性,然后在所有内容输入正确后进行“授权”。


此过程称为“制作者/检查者”,可用于将任何相关数据输入系统。


在这种情况下,与POSTING表中的主要双记录集不同的单独表中的“未授权”记录将是正确的。 对于不同的业务流程,您也可以有许多这样的表。 例如,对于通过支票从系统中输入或提取资金的情况,会计师将只需要检查一个帐户。 从第二个版本(现金簿)开始,在此类操作中始终暗含暗示。 在这种情况下,在CHECK表中,只可省去一个帐户的列,而在假设的FUND TRANSFER表中,将需要两列:“发件人”和“收件人”。


这就是重复记录原理的基本误解所在。 在日常生活中,大多数人只会遇到简单的纸质会计书籍。 例如,在这种纸质书中,要考虑某个兴趣俱乐部的财务状况,每个操作只需要输入一个即可。 但是,它仍然具有隐式重复条目,因为总会有一个隐式现金簿帐户(在这种情况下,这是俱乐部),因为所有现金流始终都是输入(参与者支付的费用)或从系统中取款(支出)俱乐部)。


误解的第二个原因是,在个人帐户对帐单中,存放在帐户中的钱将被视为“贷款”,因为一个人实质上是借给接收其钱的银行。 尽管如果此人保留其分类帐,则此条目将被记录为“借方”,因为银行欠客户这笔钱。 这笔钱从用户的“支付系统”中取出并输入到银行系统中。


软件架构


最好使用OOP和分层方法来开发实现这种双重记帐系统的软件。 级别如下:


  1. 外部介面
  2. 业务逻辑
  3. 使用数据库

当然,系统的体系结构将取决于该系统应该做什么,但是,我们可以假定其中包含以下模块:


PostEntry:一个模块,用于控制POSTING表中重复条目的创建。 他负责插入记录,分配ID和时间戳。 该模块无法删除或修改记录,除了可能已经删除了不相关的计费期的旧归档记录外,其他任何模块都不应删除或修改这些记录。 对于所有其他模块,POSTING表应该是只读的。


MakeDeposit,MakeWithdrawal,MakeTransfer:这些模块实现了资金转帐操作的基本业务逻辑。 他们将使用PostEntry模块将其结果输入数据库。


ChequeEntry和ChequeAuthorisation,ReceiveBACS( 注意:BACS是银行间支付系统 ):这些模块将系统与外界连接并提供高级接口。 他们将使用业务层模块执行其功能。 在这种情况下,无论ChequeEntry和ReceiveBACS都将通过相同的MakeDeposit工作,您都可以保证正确的处理方式而与数据输入方法无关。


取决于系统的复杂性和使用对象设计原理的期望纯度,可以或多或少地应用这种用于分离层的方法。 同时,例如,允许报表生成模块(例如TestTrialBalance)从接口级别直接访问数据库可能有意义,而不是在业务和数据库层上创建中间模块。



试用余额


“试算平衡表”-验证会计系统完整性的主要方法。 如果所有条目均根据重复条目规则输入系统,并且没有错误,则所有条目的总和应为零。 通常,几个单独的错误在无效的基础上加起来并总计为零的可能性很小,因此可以忽略不计。


最好的检查方法是从上层到下层的一致移动。 检查按此顺序有意义:


  1. POSTING.Amount列中所有值的总和
    如果发现错误(值不为零),则:
  2. 所有POSTING.Amount值的总和,但针对不同类型的值和结算期分别计算
    在此阶段,应该更清楚在系统的哪个部分发生了错误。
  3. 在“日记”表中检查单个操作。 由于日记表中每个事务中所有POSTING.Account的总和也应为零,因此您可以跟踪特定的有问题的事务。

期刊类型


journal表包含实体的简单表示,但是实际上通常证明它们更复杂并且涉及各种关系。


有时将一张桌子分成几张是有意义的。 例如,在MATERIALIZED和DEMATERIALISED上,它们可能具有一组不同的列,例如,实体可能需要其当前位置的数据。


或者,可以在一个表中存储值的不同子类型,例如货币或证券,每个子类型可以具有自己的一组属性和属性。


可以通过以下四种方式之一在数据库中组织同时具有子类型和超类型的实体(这对于任何数据库都是相当标准的情况):


  1. 一个常见的大表,其中包含许多用于子类型属性的可选列
  2. 每个子类型都有单独的表,并复制所有公共列
  3. 单独的实体,以便将超类型存储在单独的表中,并与仅包含特定于子类型的列的其他表联接
  4. 与3中相同,但子类型表中的超类型列重复

四个选项中的每个都有其优点和缺点。 从两次输入的角度来看,为POSTING条目提供一个公用表是很有用的。 选项1更适合于简单的会计系统(如本文示例中那样,值类型的唯一区别是由journal.Type列确定的)。 选项3可能更适合于具有很大不同值范围的复杂系统。

Source: https://habr.com/ru/post/zh-CN480394/


All Articles