会计余额。分层余额和汇总

Accounting Balances. Hierarchial Balances and Rollup

帐户(link 到其父帐户的主帐户列表(相同 table)) (帐户 table 使用 nested_set 插件维护,因此左、右、深度在 table 中可用,并在 adding/editing 帐户中维护)

| id | name           | parent_id |
|----|----------------|-----------|
| 1  | Assets         | null      |
| 2  | Current Assets | 1         |
| 3  | Fixed Assets   | 1         |
| 4  | Bank           | 2         |
| 5  | Bank One       | 4         |
| 6  | Bank Two       | 4         |
| 7  | Revenue        | null      |
| 8  | Sales          | 7         |
| 9  | Expenses       | null      |
| 10 | Rent           | 9         |


条目(存储每笔交易的日期和描述)

| entry_id | date       | description        |
|----------|------------|--------------------|
| 1        | Mar 3 2020 | Cash Sales         |
| 2        | Mar 3 2020 | Cash Paid For Rent |
| 3        | Apr 1 2020 | Owner Withdrawal   |

金额(存储复式记账交易的地方)

| entry_id | account_id | type   | amount |
|----------|------------|--------|--------|
| 1        | 5          | debit  | 10000  |
| 1        | 8          | credit | 10000  |
| 2        | 10         | debit  | 1000   |
| 2        | 5          | credit | 1000   |
|          |            |        |        |

鉴于上述结构,这是我的要求

  1. 以树状(分层)结构排列账户并计算个人账户余额(余额可以是debit_balance或credit_balance)
  2. 分层账户余额,将子余额汇总到父账户

PS: 我确实有上面的 req 1 的解决方案,使用的组合 WITH RECURSIVE sql 函数在账户 table 上分层排列行,然后将结果集与金额 table 相加,金额列(按类型分组后)为每个账户求和. 我很想看看这里的人将如何解决这个问题。 (让我知道你是否想看看到目前为止我得到了什么)

这是为我提供第一个结果集的查询。 (为了简洁起见,我在原始问题中省略了 normal_credit_blance 标志等细节)

        select id, parent_id, name, newdepth as depth, debit_amount, credit_amount, type,
        CASE WHEN normal_credit_balance = true THEN  credit_amount - debit_amount END as credit_balance,
            CASE WHEN normal_credit_balance = false THEN  debit_amount - credit_amount END as debit_balance

        from
        (
        WITH RECURSIVE children AS (
        SELECT id, parent_id, display_name, lft, rgt, type, normal_credit_balance, 0 as newdepth
        FROM accounts
        WHERE parent_id is null 
        UNION
        SELECT op.id, op.parent_id, op.display_name, op.lft, op.rgt, op.type, op.normal_credit_balance, newdepth + 1
        FROM accounts op
        JOIN children c ON op.parent_id = c.id
        )
        SELECT *
        FROM children 
        ) accounts_tbl 
        left join 

        ( SELECT  account_id, 
        SUM( CASE WHEN am.type =  'debit' THEN COALESCE( AMOUNT , 0.0 ) ELSE 0.0 END ) AS debit_amount ,
          SUM( CASE WHEN am.type =  'credit' THEN   COALESCE( AMOUNT , 0.0 ) ELSE 0.0 END ) AS credit_amount
          FROM amounts am
        join accounts ac on ac.id = am.account_id
          group by account_id, ac.name, ac.type ) 
          
        as  amount_tbl  

        on accounts_tbl.id = amount_tbl.account_id order by lft

基于金额 table 个条目的示例结果,汇总应如下所示:

| id | name           | balance   |  
|----|----------------|-----------|            
| 1  | Assets         | 9000      |            
| 2  | Current Assets | 9000      |            
| 3  | Fixed Assets   | 0         |            
| 4  | Bank           | 9000      |            
| 5  | Bank One       | 9000      |            
| 6  | Bank Two       | 0         |            
| 7  | Revenue        | 10000     |
| 8  | Sales          | 10000     |
| 9  | Expenses       | 1000      |
| 10 | Rent           | 1000      |


如果您使用的是闭包 table 而不是嵌套集(就像我在 中所做的那样),那么您可以使用简单的 JOIN,例如

SELECT 
  accounts.id, 
  accounts.title, 
  SUM(COALESCE(debits.amount,0)) AS debit, 
  SUM(COALESCE(credit.amount,0)) AS credit
FROM account_tree
LEFT JOIN accounts ON ancestor_id = accounts.id
LEFT JOIN balances AS debits ON account_id = child_id AND type = 'debit'
LEFT JOIN balances AS credits ON account_id = child_id AND type = 'credit'
GROUP BY accounts.id,accounts.title

作为旁注,我建议您将借方和贷方保留在同一行 - 作为 table 余额中的 2 列。

我将从计算每个帐户的“直接”余额开始,使用 left join 和聚合。然后是递归查询:你只需要从叶子遍历树到根,在你走的时候合并平衡。最后一步是聚合。

with recursive
    data (id, name, parent_id, balance) as (
        select 
            ac.*, 
            coalesce(sum(case am.type when 'debit' then - amount when 'credit' then amount end), 0) balance
        from accounts ac
        left join amounts am on am.account_id = ac.id
        group by ac.id
    ),
    cte (id, name, parent_id, balance) as (
        select d.* from data d
        union all
        select d.id, d.name, d.parent_id, d.balance + c.balance
        from cte c
        inner join data d on d.id = c.parent_id
    )
select id, name, sum(balance) from cte group by id, name

我不明白你的结果集中的所有账户最终都是正余额,而有些账户的借方多于贷方(vice-versa)。该查询将借方视为负数,将贷方视为正数。

Demo on DB Fiddle:

id | name           |   sum
-: | :------------- | ----:
 1 | Assets         | -9000
 2 | Current Assets | -9000
 3 | Fixed Assets   |     0
 4 | Bank           | -9000
 5 | Bank One       | -9000
 6 | Bank Two       |     0
 7 | Revenue        | 10000
 8 | Sales          | 10000
 9 | Expenses       | -1000
10 | Rent           | -1000