在复式记账应用程序中确保平衡交易的最佳方法是什么?

问题描述 投票:0回答:3

在复式记账中确保交易始终平衡的最佳方法是什么?

我正在 Django 中创建一个复式记账应用程序。 我有这些型号:

class Account(models.Model):
    TYPE_CHOICES = (
        ('asset', 'Asset'),
        ('liability', 'Liability'),
        ('equity', 'Equity'),
        ('revenue', 'Revenue'),
        ('expense', 'Expense'),
    )

    num = models.IntegerField()
    type = models.CharField(max_length=20, choices=TYPE_CHOICES, blank=False)
    description = models.CharField(max_length=1000)


class Transaction(models.Model):
    date = models.DateField()
    description = models.CharField(max_length=1000)
    notes = models.CharField(max_length=1000, blank=True)


class Entry(models.Model):
    TYPE_CHOICES = (
        ('debit', 'Debit'),
        ('credit', 'Credit'),
    )

    transaction = models.ForeignKey(Transaction, related_name='entries')
    type = models.CharField(max_length=10, choices=TYPE_CHOICES, blank=False)
    account = models.ForeignKey(Account, related_name='entries')
    amount = models.DecimalField(max_digits=11, decimal_places=2)

我想在模型级别强制执行平衡交易,但似乎没有在正确的位置挂钩。 例如,Transaction.clean 将不起作用,因为首先保存交易,然后由于 Entry.transactionforeignKey 而添加条目。

我也希望在管理员内部进行余额检查。 目前,我使用 EntryInlineFormSet 和干净的方法来检查管理中的余额,但这在从脚本添加交易时没有帮助。 我愿意改变我的模型以使这变得更容易。

django django-models accounting
3个回答
3
投票

(嗨瑞安!——史蒂夫·特劳戈特)

你发布这篇文章已经有一段时间了,所以我相信你已经解决了这个难题。 对于其他人和后代,我不得不说是的,你需要能够拆分交易,不,你不想采取天真的方法并假设交易腿总是成对的,因为它们不会。 您需要能够进行 N 路分割,其中 N 是任何大于 1 的正整数。Ryan 在这里有正确的结构。

Ryan 所说的 Entry 我通常称之为 Leg,如事务腿,并且我通常在某些 SQL 数据库之上使用裸 Python。 我还没有使用过 Django,但如果 Django 不支持如下内容,我会感到惊讶(震惊):我通常不会使用本机数据库行 ID 作为事务 ID,而是通常从某些数据生成唯一的事务 ID其他来源,将其存储在 Transaction 和 Leg 对象中,进行最终检查以确保借方和贷方平衡,然后在一个 SQL 事务中将 Transaction 和 Legs 提交到数据库。

Ryan,这或多或少是你最终要做的事情吗?


2
投票

这可能听起来非常天真,但为什么不将每笔交易记录在包含链接到账户表的“to account”和“from account”外键的单个记录中,而不是尝试为每笔交易创建两条记录? 在我看来,“复式记账”的本质似乎是交易总是将资金从一个账户转移到另一个账户。使用两条记录来存储此类交易没有任何优势,而且有很多缺点。


0
投票

在“迟到总比不做好”类别中,我最终做了以下事情:

class EntryQuerySet(models.QuerySet):
    ...

    @contextmanager
    def balanced(self):
        if dbtrans.get_autocommit():
            with dbtrans.atomic():
                yield
                if not self.is_balanced():
                    raise UnbalancedError()
        else:
            yield
            if not self.is_balanced():
                raise UnbalancedError()

然后我可以这样做:

transaction = Transaction.objects.create(...)
with Entry.objects.balanced():
    transaction.entries.create(account=myaccount, type='debit', amount=30)
    transaction.entries.create(account=myotheraccount, type='credit', amount=30)
© www.soinside.com 2019 - 2024. All rights reserved.