帝国ERP。 娱乐会计:总账,科目,余额

在本文中,我们将尝试渗透到“血腥企业”的核心-会计。 首先,我们将研究总账,账目和资产负债表,确定其固有属性和算法。 我们使用Python和测试驱动开发技术。 在这里,我们将进行原型制作,因此,我们将使用基本容器(列表,字典和元组)代替数据库。 该项目是根据Empire ERP项目的要求开发的


任务条件


太空...帝国星球...整个星球上的一个州。 退休2年后,人口在2周内工作2个小时。 会计科目表包含12个职位。 帐户1-4是主动的,帐户5-8是主动-被动的,帐户9-12是被动的。 公司的喇叭和蹄。 所有交易均在一个报告期内进行,期初没有余额。


项目设置


我们从github克隆项目:


git clone https://github.com/nomhoi/empire-erp.git 

我们正在使用Python 3.7.4开发。 设置虚拟环境,将其激活并安装pytest


 pip install pytest 

1.总账


转到reaserch / day1 / step1文件夹。


accounting.py


 DEBIT = 0 CREDIT = 1 AMOUNT = 2 class GeneralLedger(list): def __str__(self): res = '\nGeneral ledger' for e in self: res += '\n {:2} {:2} {:8.2f}'.format(e[DEBIT], e[CREDIT], e[AMOUNT]) res += "\n----------------------" return res 

test_accounting.py


 import pytest from accounting import * from decimal import * @pytest.fixture def ledger(): return GeneralLedger() @pytest.mark.parametrize('entries', [ [(1, 12, 100.00), (1, 11, 100.00)] ]) def test_ledger(ledger, entries): for entry in entries: ledger.append((entry[DEBIT], entry[CREDIT], Decimal(entry[AMOUNT]))) assert len(ledger) == 2 assert ledger[0][DEBIT] == 1 assert ledger[0][CREDIT] == 12 assert ledger[0][AMOUNT] == Decimal(100.00) assert ledger[1][DEBIT] == 1 assert ledger[1][CREDIT] == 11 assert ledger[1][AMOUNT] == Decimal(100.00) print(ledger) 

如我们所见,主要书籍以记录列表的形式呈现。 每个条目都设计为一个元组。 对于到目前为止的交易记录,我们仅使用借方和贷方帐号以及交易金额。 日期,描述和其他信息尚不需要,我们将在以后添加。


在测试文件创建了分类帐锁存器和参数化测试test_ledger 。 在entrys测试参数中,我们立即传输整个交易清单。 为了进行检查,我们在终端中执行pytest -s -v命令。 测试应该通过,我们将在终端中看到总分类帐中存储的交易的完整列表:


 General ledger 1 12 100.00 1 11 100.00 

2.帐户


现在,我们将发票支持添加到项目中。 转到文件夹day1 / step2


accounting.py


 class GeneralLedger(list): def __init__(self, accounts=None): self.accounts = accounts def append(self, entry): if self.accounts is not None: self.accounts.append_entry(entry) super().append(entry) 

GeneralLedger类重载了append方法。 当您将交易添加到书籍时,我们会立即将其添加到帐户中。


accounting.py


 class Account: def __init__(self, id, begin=Decimal(0.00)): self.id = id self.begin = begin self.end = begin self.entries = [] def append(self, id, amount): self.entries.append((id, amount)) self.end += amount class Accounts(dict): def __init__(self): self.range = range(1, 13) for i in self.range: self[i] = Account(i) def append_entry(self, entry): self[entry[DEBIT]].append(entry[CREDIT], Decimal(entry[AMOUNT])) self[entry[CREDIT]].append(entry[DEBIT], Decimal(-entry[AMOUNT])) 

Accounts类被设计为字典。 在键中,是帐号,在值中是帐号的内容,即 Account类的实例,该类又包含期初和期末余额字段以及与此帐户相关的交易列表。 请注意,在此列表中,借方和贷方交易的金额存储在一个字段中,借方金额为正,借方金额为负。


test_accounting.py


 @pytest.fixture def accounts(): return Accounts() @pytest.fixture def ledger(accounts): return GeneralLedger(accounts) 

在测试文件中,我们添加了帐户锁定并调整了分类帐锁定。


test_accounting.py


 @pytest.mark.parametrize('entries', [ [(1, 12, 100.00), (1, 11, 100.00)] ]) def test_accounts(accounts, ledger, entries): for entry in entries: ledger.append((entry[DEBIT], entry[CREDIT], Decimal(entry[AMOUNT]))) assert len(ledger) == 2 assert ledger[0][DEBIT] == 1 assert ledger[0][CREDIT] == 12 assert ledger[0][AMOUNT] == Decimal(100.00) assert len(accounts) == 12 assert accounts[1].end == Decimal(200.00) assert accounts[11].end == Decimal(-100.00) assert accounts[12].end == Decimal(-100.00) print(ledger) print(accounts) 

添加了一个新测试test_accounts


运行测试并观察输出:


 General ledger 1 12 100.00 1 11 100.00 ---------------------- Account 1 beg: 0.00 0.00 12: 100.00 0.00 11: 100.00 0.00 end: 200.00 0.00 ---------------------- Account 11 beg: 0.00 0.00 1: 0.00 100.00 end: 0.00 100.00 ---------------------- Account 12 beg: 0.00 0.00 1: 0.00 100.00 end: 0.00 100.00 ---------------------- 

AccountAcconts类中, __str__方法也很重载,您可以在项目源代码中看到。 为了更清楚地说明过帐和结余,列在两个列中:借方和贷方。


3.帐户:过帐验证


我们回顾以下规则:


         .         .   -         . 

也就是说,在Account类的实例中,活动帐户最终值(最终余额)不能为负,而在被动帐户上则不能为正。


转到文件夹day1 / step3


accounting.py


 class BalanceException(Exception): pass 

添加了BalanceException


 class Account: ... def is_active(self): return True if self.id < 5 else False def is_passive(self): return True if self.id > 8 else False ... 

将检查添加到“ 帐户”类,以确定该帐户是主动帐户还是被动帐户。


 class Accounts(dict): ... def check_balance(self, entry): if self[entry[CREDIT]].end - Decimal(entry[AMOUNT]) < 0 and self[entry[CREDIT]].is_active(): raise BalanceException('BalanceException') if self[entry[DEBIT]].end + Decimal(entry[AMOUNT]) > 0 and self[entry[DEBIT]].is_passive(): raise BalanceException('BalanceException') ... 

如果添加新交易后,如果在活动帐户上生成了负借方值,则将添加支票到Accounts.py类中,将引发一个例外;如果在被动帐户中获得了负的贷方值,则也会出现同样的情况。


 class GeneralLedger(list): ... def append(self, entry): if self.accounts is not None: self.accounts.check_balance(entry) self.accounts.append_entry(entry) super().append(entry) ... 

GeneralLedger类中在将过帐添加到帐户之前,我们进行检查。 如果引发异常,则过帐不会属于帐户或总分类帐。


test_accounting.py


 @pytest.mark.parametrize('entries, exception', [ ([(12, 1, 100.00)], BalanceException('BalanceException')), ([(12, 6, 100.00)], BalanceException('BalanceException')), ([(12, 11, 100.00)], BalanceException('BalanceException')), ([(6, 2, 100.00)], BalanceException('BalanceException')), #([(6, 7, 100.00)], BalanceException('BalanceException')), #([(6, 12, 100.00)], BalanceException('BalanceException')), ([(1, 2, 100.00)], BalanceException('BalanceException')), #([(1, 6, 100.00)], BalanceException('BalanceException')), #([(1, 12, 100.00)], BalanceException('BalanceException')), ]) def test_accounts_balance(accounts, ledger, entries, exception): for entry in entries: try: ledger.append((entry[DEBIT], entry[CREDIT], Decimal(entry[AMOUNT]))) except BalanceException as inst: assert isinstance(inst, type(exception)) assert inst.args == exception.args else: pytest.fail("Expected error but found none") assert len(ledger) == 0 assert len(accounts) == 12 

test_accounts_balance测试添加到测试模块 。 事务列表首先列出了所有可能的事务组合,并注释掉了没有引发异常的所有事务。 运行测试,并确保其余5个发布选项引发BalanceException


4.平衡


转到文件夹day1 / step4


accounting.py


 class Balance(list): def __init__(self, accounts): self.accounts = accounts self.suma = Decimal(0.00) self.sump = Decimal(0.00) def create(self): self.suma = Decimal(0.00) self.sump = Decimal(0.00) for i in self.accounts.range: active = self.accounts[i].end if self.accounts[i].end >= 0 else Decimal(0.00) passive = -self.accounts[i].end if self.accounts[i].end < 0 else Decimal(0.00) self.append((active, passive)) self.suma += active self.sump += passive 

创建余额时,我们只需要从一个表中的所有帐户中收集余额即可。


test_accounting.py


 @pytest.fixture def balance(accounts): return Balance(accounts) 

创建一个平衡闩锁。


 @pytest.mark.parametrize('entries', [ [ ( 1, 12, 200.00), # increase active and passive ],[ ( 1, 12, 200.00), # increase active and passive (12, 1, 100.00), # decrease passive and decrease active ],[ ( 1, 12, 300.00), # increase active and passive (12, 1, 100.00), # decrease passive and decrease active ( 2, 1, 100.00), # increase active and decrease active ],[ ( 1, 12, 300.00), # increase active and passive (12, 1, 100.00), # decrease passive and decrease active ( 2, 1, 100.00), # increase active and decrease active (12, 11, 100.00), # decrease passive and increase passive ] ]) def test_balance(accounts, ledger, balance, entries): for entry in entries: ledger.append(entry) balance.create() print(accounts) print(balance) 

我们创建了test_balance测试。 在参数列表中,列出了所有可能的交易类型:增加资产和负债,减少资产和负债,增加资产和减少资产,增加负债和减少负债。 发出4个发布选项,以便您可以逐步查看输出。 对于最后一个选项,输出如下:


 General ledger 1 12 300.00 12 1 100.00 2 1 100.00 12 11 100.00 ---------------------- Account 1 beg: 0.00 0.00 12: 300.00 0.00 12: 0.00 100.00 2: 0.00 100.00 end: 100.00 0.00 ---------------------- Account 2 beg: 0.00 0.00 1: 100.00 0.00 end: 100.00 0.00 ---------------------- Account 11 beg: 0.00 0.00 12: 0.00 100.00 end: 0.00 100.00 ---------------------- Account 12 beg: 0.00 0.00 1: 0.00 300.00 1: 100.00 0.00 11: 100.00 0.00 end: 0.00 100.00 ---------------------- Balance 1 : 100.00 0.00 2 : 100.00 0.00 3 : 0.00 0.00 4 : 0.00 0.00 5 : 0.00 0.00 6 : 0.00 0.00 7 : 0.00 0.00 8 : 0.00 0.00 9 : 0.00 0.00 10 : 0.00 0.00 11 : 0.00 100.00 12 : 0.00 100.00 ---------------------- sum: 200.00 200.00 ====================== 

5.反转


现在,让我们检查一下反转是如何执行的。


 @pytest.mark.parametrize('entries', [ [ ( 1, 12, 100.00), ( 1, 12,-100.00), ] ]) def test_storno(accounts, ledger, balance, entries): for entry in entries: ledger.append(entry) balance.create() print(ledger) print(accounts) print(balance) 

结论如下:


 General ledger 1 12 100.00 1 12 -100.00 ---------------------- Account 1 beg: 0.00 0.00 12: 100.00 0.00 12: 0.00 100.00 end: 0.00 0.00 ---------------------- Account 12 beg: 0.00 0.00 1: 0.00 100.00 1: 100.00 0.00 end: 0.00 0.00 ---------------------- Balance 1 : 0.00 0.00 2 : 0.00 0.00 3 : 0.00 0.00 4 : 0.00 0.00 5 : 0.00 0.00 6 : 0.00 0.00 7 : 0.00 0.00 8 : 0.00 0.00 9 : 0.00 0.00 10 : 0.00 0.00 11 : 0.00 0.00 12 : 0.00 0.00 ---------------------- sum: 0.00 0.00 ====================== 

一切似乎都是正确的。


如果我们使用这样的一组帖子,则测试将通过:


 ( 1, 12, 100.00), (12, 1, 100.00), ( 1, 12,-100.00), 

如果是这样的集合,则将最后两行交换到适当的位置,那么我们将得到一个例外:


 ( 1, 12, 100.00), ( 1, 12,-100.00), (12, 1, 100.00), 

因此,为了捕获这种错误,必须在更正交易之后立即进行冲销。


结论


在以下文章中,我们将继续进行会计研究,并将根据Empire ERP的要求清单考虑系统开发的所有方面。

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


All Articles