# Group Ledger Entries

Source: https://fragment.dev/guides/group-ledger-entries

[Documentation index](https://fragment.dev/llms.txt)

<a id="group-ledger-entries"></a>

A Ledger Entry Group is a collection of related Ledger Entries that occur at different points in time. Each Group tracks the net change to each Ledger Account balance it affects.

Use Ledger Entry Groups to tie together Ledger Entries that are part of the same funds flow, such as a deposit, settlement or invoice. To store metadata, use [tags](https://fragment.dev/markdown/guides/store-metadata.md) instead.

<a id="configuring-groups"></a>

## Configuring Groups 

Groups for a Ledger Entry Type are defined as a list of key/value pairs in the Schema:

```json title="Ledger Entry Type with Groups"
{
  "type": "user_initiates_withdrawal",
  "description": "{{user_id}} initiates withdrawal",
  "lines": [
    {
      "account": {
        "path": "liabilities/users:{{user_id}}/available"
      },
      "key": "decrease_user_balance",
      "amount": "-{{withdraw_amount}}"
    },
    {...other line}
  ],
  "groups": [
    {
      "key": "withdrawal",
      "value": "{{withdrawal_id}}"
    }
  ]
}
```

Ledger Entry Groups have the following limitations:
- You can specify up to 10 Groups for any Ledger Entry type.
- Parameters can be used in the `value` of a Group, but not the `key`.
- Group keys must be unique per Ledger Entry, and you can only provide one value for each key.

<a id="querying-balances"></a>

## Querying balances

Use the `ledgerEntryGroup.balances` expansion to get the net change per Ledger Account balance from all Ledger Entries in a Group. Group balances are eventually consistent.

```graphql title="GetLedgerEntryGroupBalances"
query GetLedgerEntryGroupBalances(
  $ledger: LedgerMatchInput!
  $entryGroup: EntryGroupMatchInput!,
) {
  ledger(ledger: $ledger) {
    ledgerEntryGroup(ledgerEntryGroup: $entryGroup) {
      balances {
        nodes {
          account {
            path
          }
          ownBalance
        }
      }
    }
  }
}
```

```json title="GetLedgerEntryGroupBalances Variables"
{
  "entryGroup": {
    "key": "withdrawal",
    "value": "some-withdrawal-id"
  },
  "ledger": {
    "ik": "quickstart-ledger"
  }
}
```

The response for a Ledger Entry Group with a settled withdrawal:

```json title="GetLedgerEntryGroupBalances Response"
{
  "data": {
    "ledger": {
      "ledgerEntryGroup": {
        "balances": {
          "nodes": [
            {
              "account": {
                "path": "asset-root/bank"
              },
              "ownBalance": "-50000"
            },
            {
              "account": {
                "path": "liability-root/user:user-id/available"
              },
              "ownBalance": "-50000"
            },
            {
              "account": {
                "path": "liability-root/user:user-id/pending"
              },
              "ownBalance": "0"
            }
          ]
        }
      }
    }
  }
}
```

<a id="querying-balances-consistent-balances"></a>

### Consistent balances
Ledger Entry Groups support strongly-consistent reads for the ownBalance field. See [Consistent Ledger Entry Group balances](https://fragment.dev/markdown/guides/configure-consistency.md#consistent-groups) for more information on how to configure this in your Schema. See [Query Consistent Groups](https://fragment.dev/markdown/guides/configure-consistency.md#query-consistent-groups) for how to query strongly consistent Ledger Entry Group balances.

<a id="querying-balances-filtering-balances"></a>

### Filtering balances

Balances in a Group may be filtered by account, currency, and ownBalance.

```graphql title="QueryLedgerEntryGroupBalances"
query QueryLedgerEntryGroupBalances(
  $ledger: LedgerMatchInput!
  $entryGroup: EntryGroupMatchInput!,
  $filter: LedgerEntryGroupBalanceFilterSet,
) {
  ledger(ledger: $ledger) {
    ledgerEntryGroup(ledgerEntryGroup: $entryGroup) {
      balances(filter: $filter) {
        nodes {
          account {
            path
          }
          ownBalance
          currency {
            code
          }
        }
      }
    }
  }
}
```

```json title="QueryLedgerEntryGroupBalances Variables"
{
  "entryGroup": {
    "key": "withdrawal",
    "value": "12345"
  },
  "ledger": {
    "ik": "quickstart-ledger"
  },
  "filter": {
    "currency": {
      "equalTo": { "code": "USD" }
    },
    "ownBalance": {
      "gte": "-1000",
      "lte": "1000"
    },
    "account": {
      "path": {
        "equalTo": "liability-root/user:user-id/pending"
      }
    }
  }
}
```

The response is: 
```json title="QueryLedgerEntryGroupBalances Response"
{
  "data": {
    "ledger": {
      "ledgerEntryGroup": {
        "balances": {
          "nodes": [
            {
              "account": {
                "path": "liability-root/user:user-id/pending"
              },
              "ownBalance": "0",
              "currency": {
                "code": "USD"
              }
            }
          ]
        }
      }
    }
  }
}
```

<a id="querying-balances-filter-by-template"></a>

### Filter by template

Group balances support filtering account paths using '*' in place of a template variable.

```graphql title="QueryLedgerEntryGroupBalances"
query QueryLedgerEntryGroupBalances(
  $ledger: LedgerMatchInput!
  $entryGroup: EntryGroupMatchInput!,
  $filter: LedgerEntryGroupBalanceFilterSet,
) {
  ledger(ledger: $ledger) {
    ledgerEntryGroup(ledgerEntryGroup: $entryGroup) {
      balances(filter: $filter) {
        nodes {
          account {
            path
          }
          ownBalance
        }
      }
    }
  }
}
```

```json title="QueryLedgerEntryGroupBalances Variables"
{
  "entryGroup": {
    "key": "withdrawal",
    "value": "12345"
  },
  "ledger": {
    "ik": "quickstart-ledger"
  },
  "filter": {
    "account": {
      "path": {
        "matches": "liability-root/user:*/pending"
      }
    }
  }
}
```

The response is: 
```json title="QueryLedgerEntryGroupBalances Response"
{
  "data": {
    "ledger": {
      "ledgerEntryGroup": {
        "balances": {
          "nodes": [
            {
              "account": {
                "path": "liability-root/user:user-id/pending"
              },
              "ownBalance": "0"
            },
            {
              "account": {
                "path": "liability-root/user:another-user-id/pending"
              },
              "ownBalance": "2500"
            }
          ]
        }
      }
    }
  }
}
```

<a id="updating-entry-groups"></a>

## Updating Entry Groups

In addition to Groups defined in your Schema, you can add a posted Ledger Entry to additional Groups.

```graphql title="updateLedgerEntry mutation" expandable=true
mutation UpdateLedgerEntryGroups(
  $ledgerEntry: LedgerEntryMatchInput!
  $update: UpdateLedgerEntryInput!
) {
  updateLedgerEntry(
    ledgerEntry: $ledgerEntry,
    update: $update
  ) {
    __typename
    ... on UpdateLedgerEntryResult {
      entry {
        type
        ik
        groups {
          key
          value
        }
        lines {
          nodes {
            amount
            description
            account {
              path
            }
          }
        }
      }
    }
    ... on Error {
      code
      message
    }
  }
}
```

```json title="updateLedgerEntry variables to add and update groups"
{
  "ledgerEntry": {
    "ik": "add-ledger-entry",
    "ledger": {
      "ik": "quickstart-ledger"
    }
  },
  "update": {
    "groups": [
      {
        "key": "withdrawal",
        "value": "12345"
      }
    ]
  }
}
```

This is an additive operation:
- If you specify a new Group, this Ledger Entry will be added to that Group
- If you don't specify an existing Group, this Ledger Entry will remain in that Group
- You may not modify an existing Group key or remove an Ledger Entry from a Group

You can only update a Ledger Entry a maximum of 10 times.
