如何设计数据库来跟踪欠条和付款

时间:2017-02-13 15:13:43

标签: ruby-on-rails database database-design accounting

问题

如何设计一个可以跟踪两个用户之间的余额的数据库,以及保留所有交易的日志,而不会重复数据?

奖励: Ruby on Rails引用将非常棒

详细信息

我正在尝试制作一个可以跟踪人们的IOU的webapp,但是我很难绕过如何建模数据库。这些都是硬性要求

  • 用户可以相互欠款 例如爱丽丝欠鲍勃10美元
  • 用户可以部分还清对方的钱 例如爱丽丝向鲍勃支付了3美元,还欠鲍勃7美元
  • 用户可以支付超过欠款,从而成为贷方自己 例如爱丽丝支付鲍勃15美元,现在鲍勃欠爱丽丝8美元(10 - 3 - 15 = -8)
  • 用户可以查看他们欠下的用户列表或欠他们的用户列表 例如Bob欠Alice总共10美元,8美元,Charles <$ li> 2美元
  • 用户可以查看其交易日志 例如爱丽丝从鲍勃那里拿走了10美元,然后给了鲍勃3美元,然后给了鲍勃15美元

API

User个对象具有以下方法:

  • User#account_info:用户帐户的摘要
    • 返回:
      • amount_payable:用户欠款
      • amount_receivable:欠用户的金额
  • User#payables:用户所欠人数和金额的列表
    • 返回一个数组:
      • user:欠下的另一个
      • amount:欠款
  • User#receivables用户ID所欠人数和金额的列表
    • 返回一个数组:
      • user:欠我们的另一个
      • amount:欠款
  • User#transactions过去交易清单:
    • 返回一个数组:
      • typegivetake
      • user:此次交易中的另一个
      • amount:欠款
  • User#give:给别人钱
    • PARAMS:
      • user:此次交易中的另一个
      • amount:欠款
    • 退货: nil
  • User#take:从别人那里拿钱
    • PARAMS:
      • user:此次交易中的另一个
      • amount:欠款
    • 退货: nil

行为

以下是用户之间的互动如何发挥

初始状态

### Initialize new users
a = User.new
b = User.new
c = User.new

## Users owe and are owed nothing
a.account_info
# {amount_payable: 0, amount_receivable: 0}
b.account_info
# {amount_payable: 0, amount_receivable: 0}

## Users have no payables or receivables
a.receivables
# []
b.receivables
# []

## Users have no transactions
a.transactions
# []
b.transactions
# []

A从B

获得10美元
a.takes(from: b, amount: 10)
# nil

## A now owes $10
a.account_info
# {amount_payable: 10, amount_receivable: 0}
# B is now owed $10
# {amount_payable: 0, amount_receivable: 10}

a.payables
# [{user: b.id, amount: 5}}]
b.receivables
# [{user: a.id, amount: 5}}]


## A has transactions
a.transactions
# [{type: 'take', user: b.id, amount: 10}]
## B has transactions
b.transactions
# [{type: 'give', user: a.id, amount: 10}]

A给B $ 10回复

a.gives(to: b, amount: 10)
#nil

# a now owes $5
a.account_info
# {amount_payable: 10, amount_receivable: 0}
# b is now owed $5
# {amount_payable: 0, amount_receivable: 10}

a.transactions
# [{type: 'give', user: b.id, amount: 5}, {type: 'take', user: b.id, amount: 10}]
## B has transactions
b.transactions
# [{type: 'take', user: a.id, amount: 5}, {type: 'give', user: a.id, amount: 10}]

a.payables
# [{user: b.id, amount: 5}}]
b.receivables
# [{user: a.id, amount: 5}}]

可能的解决方案

(这花了我三天时间和无数头痛)

这个解决方案非常hacky

def User << ActiveRecord::Base
  has_one :wallet
end

def Wallet << ActiveRecord::Base
  belongs_to :user
  has_many :accounts
end

def Account << ActiveRecord::Base
  has_many :transfers
  # balance - integer
  # creditor - user_id, class: Wallet
  # debtor - user_id. class: Wallet
end

def Transfer << ActiveRecord::Base
  belongs_to :account
  # amount - integer
  # datetime - datetime
end

鉴于以上模型:

神奇发生在Accounts模型中:

  • 当余额为正数时,债权人欠款。
  • 余额为负数时欠债务人。

交易是转移的运行日志:

  • 转账中的正数表示债权人向债务人汇款
  • 负数是从债务人到债权人的钱。

这样,我们在以下情况下确定用户欠款:

  • 他们是债权人,余额是积极的;或
  • 他们是债务人,余额是负面的

3 个答案:

答案 0 :(得分:2)

老实说,现在不想写出所有这些问题,所以我只想指出你想要的方向。

首先,你的表格过于复杂

# Tables:

def User << ActiveRecord::Base
  has_one :wallet
  has_many :transactions
  # General user info
  # wallet_id
end


def Transfers << ActiveRecord::Base
  belongs_to :creditor (user)
  belongs_to :debtor (user)
  # amount - integer
  # timestamps
  # creditor_id
  # debtor_id
end

这就是你所需要的一切。

您需要使用ActiveRecord::Base.transactions do来更新帐户。

在您的用户类中构建.gives函数。

User.gives(to: b, amount: 10)
  # Create transfer 
  # Transfer.create({ amount:10, creditor: a.id, debtor: b.id })

在您的用户类中构建.takes函数。

User.takes(from: b, amount: 10)
  # Create transfer 
  # Transfer.create({ amount:10, creditor: b.id, debtor: a.id })

转移是一个简单的有很多关系

User.Transfers
  has_many :Transfers

应付账款,应收账款,Account_info是一个更深入的SQL查询,但没有什么太疯狂。它会看起来像这样(不完全),如果你不能从下面这个评论中得到它。今晚我可以回来尝试解决这些问题。

a.payables
  # SQL query
  # SELECT  SUM(amount_payable.amount) - SUM (amount_receivable.amount) 
  # FROM User
  # WHERE User = a
  # JOIN Transfer AS amount_payable ON Creditor == a
  # JOIN Transfer AS amount_receivable ON Creditor == amount_payable.debitor
  # GROUPBY amount_payable.debitor
  # HAVING SUM(amount_payable.amount) - SUM (amount_receivable.amount) > 0

User.receivables会反转债权人和债务人的位置。

Account_Info会在连接表上加倍,并具有更多特定的AS语句

答案 1 :(得分:0)

我对RoR一无所知,所以我不能用这种方式说出来。但这是一个相当简单的数据库问题。您只需要三个表:用户,贷款和付款。每笔贷款都有贷款人和贷款人。每笔贷款都有付款。

用户表只需要一个唯一的ID和有关用户的信息。

贷款表将有两个用户表的外键,一个用于贷方,一个用于收件人。这将包括贷款金额。它会在借钱时创造出来。对于单个贷方或收件人用户,此表可以有任意数量的条目。

付款表只是一笔贷款付款清单。它有一个到贷款表的外键链接。这是一对多关系的多方面。

因此,要了解用户借出的贷款,请对用户和贷款进行查询,并将其与贷款中的贷方外键相关联。同样,对于已收到贷款的用户,您使用收件人外键。

我不会将贷款余额存入贷款表,因为那时你有两个事实来源 - 支付表和贷款表。最好只在需要时计算剩余的贷款余额。关系数据库非常擅长在非常大的数据集上执行此操作。

答案 2 :(得分:0)

你做这件事的方式很好,但我会做一些调整。除非有某些特定原因,否则您不一定需要钱包课程。您可以在用户类本身中跟踪它们的平衡。

内部账户,我没有债权人和债务人ID,我只有2个user_id,保持余额为正,并跟踪谁是欠债的人。这样你就不需要摆弄负片,并简化了大量的簿记工作。