Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/illuminatech/balance

Balance accounting (bookkeeping) system based on debit and credit principle
https://github.com/illuminatech/balance

accounting balance bookkeeping credit debit laravel

Last synced: about 2 months ago
JSON representation

Balance accounting (bookkeeping) system based on debit and credit principle

Awesome Lists containing this project

README

        





Balance Accounting System extension for Laravel



This extension provides basic support for balance accounting (bookkeeping) system based on [debit and credit](https://en.wikipedia.org/wiki/Debits_and_credits) principle.

For license information check the [LICENSE](LICENSE.md)-file.

[![Latest Stable Version](https://img.shields.io/packagist/v/illuminatech/balance.svg)](https://packagist.org/packages/illuminatech/balance)
[![Total Downloads](https://img.shields.io/packagist/dt/illuminatech/balance.svg)](https://packagist.org/packages/illuminatech/balance)
[![Build Status](https://github.com/illuminatech/balance/workflows/build/badge.svg)](https://github.com/illuminatech/balance/actions)

Installation
------------

The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run

```
php composer.phar require --prefer-dist illuminatech/balance
```

or add

```json
"illuminatech/balance": "*"
```

to the require section of your composer.json.

Usage
-----

This extension provides basic support for balance accounting (bookkeeping) system based on [debit and credit](https://en.wikipedia.org/wiki/Debits_and_credits) principle.
Balance system is usually used for the accounting (bookkeeping) and money operations. However, it may also be used for any
resource transferring from one location to another. For example: transferring goods from storehouse to the shop and so on.

There 2 main terms related to the balance system:

- account - virtual storage of the resources, which have some logical meaning.
- transaction - represents actual transfer of the resources to or from the particular account.

Lets assume we have a system, which provides virtual money balance for the user. Money on the balance can be used for the
goods purchasing, user can top up his balance via some payment gateway. In such example, each user should have 3 virtual
balance accounts: 'virtual-money', 'payment-gateway' and 'purchases'. When user tops up his virtual balance, our system
should remove money from 'payment-gateway' and add them to 'virtual-money'. When user purchases an item, our system should
remove money from 'virtual-money' and add them to 'purchases'.
The trick is: if you sum current amount over all user related accounts ('payment-gateway' + 'virtual-money' + 'purchases'),
it will always be equal to zero. Such check allows you to verify is something went wrong any time.

This extension introduces term 'balance manager' as a service, which should handle all balance transactions.
Public contract for such manager is determined by `\Illuminatech\Balance\BalanceContract` interface.
Following particular implementations are provided:

- [\Illuminatech\Balance\BalanceDb](src/BalanceDb.php) - uses a relational database as a data storage.

Please refer to the particular manager class for more details.

This extension provides `\Illuminatech\Balance\BalanceServiceProvider` service provider, which binds `\Illuminatech\Balance\BalanceContract`
as a singleton in DI container. Thus you can get balance manager via automatic DI injections or via container instance.
For example:

```php
increase($accountId, $amount);

// ...
}

public function decrease($accountId, $amount)
{
$balance = Container::getInstance()->get(BalanceContract::class);

$balance->decrease($accountId, $amount);

// ...
}

// ...
}
```

You may as well use `\Illuminatech\Balance\Facades\Balance` facade. For example:

```php

This extension uses [illuminatech/array-factory](https://github.com/illuminatech/array-factory) for configuration.
Make sure you are familiar with 'array factory' concept before configuring this extension.
Configuration is stored at 'config/balance.php' file.

You can publish predefined configuration file using following console command:

```
php artisan vendor:publish --provider="Illuminatech\Balance\BalanceServiceProvider" --tag=config
```

In case you are using `\Illuminatech\Balance\BalanceDb`, you can publish predefined database migration for it
using following console command:

```
php artisan vendor:publish --provider="Illuminatech\Balance\BalanceServiceProvider" --tag=migrations
```

## Basic operations

In order to increase (debit) balance at particular account, `\Illuminatech\Balance\BalanceContract::increase()` method is used:

```php
Tip: actually, method `decrease()` is redundant, you can call `increase()` with negative amount in order to achieve the same result.

It is unlikely you will use plain `increase()` and `decrease()` methods in your application. In most cases there is a need
to **transfer** money from one account to another at once. Method `\Illuminatech\Balance\BalanceContract::transfer()` can be
used for this:

```php
Note: If you wish each transaction created by `transfer()` remember another account involved in the process, you'll need
to setup `\Illuminatech\Balance\Balance::$extraAccountLinkAttribute`.

You may revert particular transaction using `\Illuminatech\Balance\BalanceContract::revert()` method:

```php

Using account IDs for the balance manager is not very practical. In our above example, each system user have 3 virtual
accounts, each of which has its own unique ID. However, while performing purchase, we operate user ID and account type,
so we need to query actual account ID before using balance manager.
Thus there is an ability to specify account for the balance manager methods using their attributes set. For example:

```php
user();

Balance::transfer(
[
'userId' => $user->id,
'type' => 'virtual-money',
],
[
'userId' => $user->id,
'type' => 'purchases',
],
500
);
```

In this example balance manager will find ID of the affected accounts automatically, using provided attributes as a filter.

You may enable `\Illuminatech\Balance\Balance::$autoCreateAccount`, allowing automatic creation of the missing accounts, if they
are specified as attributes set. This allows accounts creation on the fly, by demand only, eliminating necessity of their
pre-creation.

**Heads up!** Actually 'account' entity is redundant at balance system, and its usage can be avoided. However, its presence
provides more flexibility and saves performance. Storing of account data is not mandatory for this extension, you can
configure your balance manager in the way it is not used.

## Finding account current balance

Current money amount at particular account can always be calculated as a sum of amounts over related transactions.
You can use `\Illuminatech\Balance\BalanceContract::calculateBalance()` method for that:

```php
select(['balance'])
->where(['id' => $fromAccountId])
->value('balance');

echo $currentBalance; // outputs: -100
```

## Saving extra transaction data

Usually there is a necessity to save extra information along with the transaction. For example: we may need to save
payment ID received from payment gateway. This can be achieved in following way:

```php
user();

// simple increase :
Balance::increase(
[
'userId' => $user->id,
'type' => 'virtual-money',
],
100,
// extra data associated with transaction :
[
'paymentGateway' => 'PayPal',
'paymentId' => 'abcxyzerft',
]
);

// transfer :
Balance::transfer(
[
'userId' => $user->id,
'type' => 'payment-gateway',
],
[
'userId' => $user->id,
'type' => 'virtual-money',
],
100,
// extra data associated with transaction :
[
'paymentGateway' => 'PayPal',
'paymentId' => 'abcxyzerft',
]
);
```

The way extra attributes are stored in the data storage depends on particular balance manager implementation.
For example: `\Illuminatech\Balance\BalanceDb` will try to store extra data inside transaction table columns, if their name
equals the parameter name. You may as well setup special data field via `\Illuminatech\Balance\BalanceDb::$dataAttribute`,
which will store all extra parameters, which have no matching column, in serialized state.

> Note: watch for the keys you use in transaction data: make sure they do not conflict with columns, which are
reserved for other purposes, like primary keys.

## Saving balance amount per transaction

There is a common accounting (bookkeeping) practice to record new balance amount per each performed transaction.
Such approach simplifies recreation of the balance transfers dynamics and search for possible errors.
You can achieve such behavior by setting `\Illuminatech\Balance\Balance::$newBalanceAttribute` value with the name of
transaction entity attribute, which should store account balance, which appears after this transaction has been performed.
For example:

```php
where(['account_id' => $accountId])
->orderBy('id', 'DESC');

Balance::increase($accountId, 50); // assume this is first time accounts is affected
$lastTransaction = $lastTransactionQuery->first();
echo $lastTransaction->new_balance; // outputs: 50

Balance::increase($accountId, 25);
$lastTransaction = $lastTransactionQuery->first();
echo $lastTransaction->new_balance; // outputs: 75

Balance::decrease($accountId, 50);
$lastTransaction = $lastTransactionQuery->first();
echo $lastTransaction->new_balance; // outputs: 25
```

## Events

`\Illuminatech\Balance\Balance` provides several events, which can be handled via event listener:

- [\Illuminatech\Balance\Events\CreatingTransaction](src/Events/CreatingTransaction.php) - raised before creating new transaction.
- [\Illuminatech\Balance\Events\TransactionCreated](src/Events/TransactionCreated.php) - raised after creating new transaction.

For example:

```php
data['amount'] += 10; // you may adjust transaction data to be saved, including transaction amount
$event->data['comment'] = 'adjusted by event handler';
});

Event::listen(TransactionCreated::class, function (TransactionCreated $event) {
echo 'new transaction: '.$event->transactionId; // you may get newly created transaction ID
});

Balance::increase(1, 100); // outputs: 'new transaction: 1'
echo Balance::calculateBalance(1); // outputs: 110
```