{"id":13542577,"url":"https://github.com/illuminatech/balance","last_synced_at":"2025-10-17T20:49:10.842Z","repository":{"id":44380694,"uuid":"169416058","full_name":"illuminatech/balance","owner":"illuminatech","description":"Balance accounting (bookkeeping) system based on debit and credit principle","archived":false,"fork":false,"pushed_at":"2024-04-19T09:54:28.000Z","size":59,"stargazers_count":151,"open_issues_count":0,"forks_count":25,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-04-25T16:44:47.418Z","etag":null,"topics":["accounting","balance","bookkeeping","credit","debit","laravel"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/illuminatech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["klimov-paul"],"patreon":"klimov_paul"}},"created_at":"2019-02-06T14:09:28.000Z","updated_at":"2024-06-21T20:18:12.131Z","dependencies_parsed_at":"2024-01-16T15:51:37.448Z","dependency_job_id":"bf4915a4-b07b-4b4a-8443-91cf877ff1b0","html_url":"https://github.com/illuminatech/balance","commit_stats":{"total_commits":42,"total_committers":2,"mean_commits":21.0,"dds":"0.023809523809523836","last_synced_commit":"7e6dc332dee4ac7947439c5f81193ab7dd8a9816"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/illuminatech%2Fbalance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/illuminatech%2Fbalance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/illuminatech%2Fbalance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/illuminatech%2Fbalance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/illuminatech","download_url":"https://codeload.github.com/illuminatech/balance/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246796873,"owners_count":20835460,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accounting","balance","bookkeeping","credit","debit","laravel"],"created_at":"2024-08-01T10:01:11.502Z","updated_at":"2025-10-17T20:49:05.801Z","avatar_url":"https://github.com/illuminatech.png","language":"PHP","funding_links":["https://github.com/sponsors/klimov-paul","https://patreon.com/klimov_paul"],"categories":["laravel","PHP"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/illuminatech\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://avatars1.githubusercontent.com/u/47185924\" height=\"100px\"\u003e\n    \u003c/a\u003e\n    \u003ch1 align=\"center\"\u003eBalance Accounting System extension for Laravel\u003c/h1\u003e\n    \u003cbr\u003e\n\u003c/p\u003e\n\nThis extension provides basic support for balance accounting (bookkeeping) system based on [debit and credit](https://en.wikipedia.org/wiki/Debits_and_credits) principle.\n\nFor license information check the [LICENSE](LICENSE.md)-file.\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/illuminatech/balance.svg)](https://packagist.org/packages/illuminatech/balance)\n[![Total Downloads](https://img.shields.io/packagist/dt/illuminatech/balance.svg)](https://packagist.org/packages/illuminatech/balance)\n[![Build Status](https://github.com/illuminatech/balance/workflows/build/badge.svg)](https://github.com/illuminatech/balance/actions)\n\n\nInstallation\n------------\n\nThe preferred way to install this extension is through [composer](http://getcomposer.org/download/).\n\nEither run\n\n```\nphp composer.phar require --prefer-dist illuminatech/balance\n```\n\nor add\n\n```json\n\"illuminatech/balance\": \"*\"\n```\n\nto the require section of your composer.json.\n\n\nUsage\n-----\n\nThis extension provides basic support for balance accounting (bookkeeping) system based on [debit and credit](https://en.wikipedia.org/wiki/Debits_and_credits) principle.\nBalance system is usually used for the accounting (bookkeeping) and money operations. However, it may also be used for any\nresource transferring from one location to another. For example: transferring goods from storehouse to the shop and so on.\n\nThere 2 main terms related to the balance system:\n\n - account - virtual storage of the resources, which have some logical meaning.\n - transaction - represents actual transfer of the resources to or from the particular account.\n\nLets assume we have a system, which provides virtual money balance for the user. Money on the balance can be used for the\ngoods purchasing, user can top up his balance via some payment gateway. In such example, each user should have 3 virtual\nbalance accounts: 'virtual-money', 'payment-gateway' and 'purchases'. When user tops up his virtual balance, our system\nshould remove money from 'payment-gateway' and add them to 'virtual-money'. When user purchases an item, our system should\nremove money from 'virtual-money' and add them to 'purchases'.\nThe trick is: if you sum current amount over all user related accounts ('payment-gateway' + 'virtual-money' + 'purchases'),\nit will always be equal to zero. Such check allows you to verify is something went wrong any time.\n\nThis extension introduces term 'balance manager' as a service, which should handle all balance transactions.\nPublic contract for such manager is determined by `\\Illuminatech\\Balance\\BalanceContract` interface.\nFollowing particular implementations are provided:\n\n - [\\Illuminatech\\Balance\\BalanceDb](src/BalanceDb.php) - uses a relational database as a data storage.\n\nPlease refer to the particular manager class for more details.\n\nThis extension provides `\\Illuminatech\\Balance\\BalanceServiceProvider` service provider, which binds `\\Illuminatech\\Balance\\BalanceContract`\nas a singleton in DI container. Thus you can get balance manager via automatic DI injections or via container instance.\nFor example:\n\n```php\n\u003c?php\n\nuse Illuminate\\Container\\Container;\nuse App\\Http\\Controllers\\Controller;\nuse Illuminatech\\Balance\\BalanceContract;\n\nclass BalanceController extends Controller\n{\n    public function increase(BalanceContract $balance, $accountId, $amount)\n    {\n        $balance-\u003eincrease($accountId, $amount);\n        \n        // ...\n    }\n    \n    public function decrease($accountId, $amount)\n    {\n        $balance = Container::getInstance()-\u003eget(BalanceContract::class);\n        \n        $balance-\u003edecrease($accountId, $amount);\n        \n        // ...\n    }\n    \n    // ...\n}\n```\n\nYou may as well use `\\Illuminatech\\Balance\\Facades\\Balance` facade. For example:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::increase($accountId, $amount);\n```\n\nIn these documentation facade is used in code snippets for simplicity.\n\n\n## Application configuration \u003cspan id=\"application-configuration\"\u003e\u003c/span\u003e\n\nThis extension uses [illuminatech/array-factory](https://github.com/illuminatech/array-factory) for configuration.\nMake sure you are familiar with 'array factory' concept before configuring this extension.\nConfiguration is stored at 'config/balance.php' file.\n\nYou can publish predefined configuration file using following console command:\n\n```\nphp artisan vendor:publish --provider=\"Illuminatech\\Balance\\BalanceServiceProvider\" --tag=config\n```\n\nIn case you are using `\\Illuminatech\\Balance\\BalanceDb`, you can publish predefined database migration for it\nusing following console command:\n\n```\nphp artisan vendor:publish --provider=\"Illuminatech\\Balance\\BalanceServiceProvider\" --tag=migrations\n```\n\n\n## Basic operations \u003cspan id=\"basic-operations\"\u003e\u003c/span\u003e\n\nIn order to increase (debit) balance at particular account, `\\Illuminatech\\Balance\\BalanceContract::increase()` method is used:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::increase($accountId, 500); // add 500 credits to account\n```\n\nIn order to decrease (credit) balance at particular account, `\\Illuminatech\\Balance\\BalanceContract:decrease()` method is used:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::decrease($accountId, 100); // remove 100 credits from account\n```\n\n\u003e Tip: actually, method `decrease()` is redundant, you can call `increase()` with negative amount in order to achieve the same result.\n\nIt is unlikely you will use plain `increase()` and `decrease()` methods in your application. In most cases there is a need\nto **transfer** money from one account to another at once. Method `\\Illuminatech\\Balance\\BalanceContract::transfer()` can be\nused for this:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\n$fromId = 1;\n$toId = 2;\nBalance::transfer($fromId, $toId, 100); // remove 100 credits from account 1 and add 100 credits to account 2\n```\n\nNote that method `transfer()` creates 2 separated transactions: one per each affected account. Thus you can easily fetch\nall money transfer history for particular account, simply selecting all transactions linked to it. 'Debit' transactions\nwill have positive amount, while 'credit' ones - negative.\n\n\u003e Note: If you wish each transaction created by `transfer()` remember another account involved in the process, you'll need\n  to setup `\\Illuminatech\\Balance\\Balance::$extraAccountLinkAttribute`.\n\nYou may revert particular transaction using `\\Illuminatech\\Balance\\BalanceContract::revert()` method:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::revert($transactionId);\n```\n\nThis method will not remove original transaction, but create a new one, which compensates it.\n\n\n## Querying accounts \u003cspan id=\"querying-accounts\"\u003e\u003c/span\u003e\n\nUsing account IDs for the balance manager is not very practical. In our above example, each system user have 3 virtual\naccounts, each of which has its own unique ID. However, while performing purchase, we operate user ID and account type,\nso we need to query actual account ID before using balance manager.\nThus there is an ability to specify account for the balance manager methods using their attributes set. For example:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\n$user = request()-\u003euser();\n\nBalance::transfer(\n    [\n        'userId' =\u003e $user-\u003eid,\n        'type' =\u003e 'virtual-money',\n    ],\n    [\n        'userId' =\u003e $user-\u003eid,\n        'type' =\u003e 'purchases',\n    ],\n    500\n);\n```\n\nIn this example balance manager will find ID of the affected accounts automatically, using provided attributes as a filter.\n\nYou may enable `\\Illuminatech\\Balance\\Balance::$autoCreateAccount`, allowing automatic creation of the missing accounts, if they\nare specified as attributes set. This allows accounts creation on the fly, by demand only, eliminating necessity of their\npre-creation.\n\n**Heads up!** Actually 'account' entity is redundant at balance system, and its usage can be avoided. However, its presence\nprovides more flexibility and saves performance. Storing of account data is not mandatory for this extension, you can\nconfigure your balance manager in the way it is not used.\n\n\n## Finding account current balance \u003cspan id=\"finding-account-current-balance\"\u003e\u003c/span\u003e\n\nCurrent money amount at particular account can always be calculated as a sum of amounts over related transactions.\nYou can use `\\Illuminatech\\Balance\\BalanceContract::calculateBalance()` method for that:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::transfer($fromAccount, $toAccount, 100); // assume this is first time accounts are affected\n\necho Balance::calculateBalance($fromAccount); // outputs: -100\necho Balance::calculateBalance($toAccount); // outputs: 100\n```\n\nHowever, calculating current balance each time you need it, is not efficient. Thus you can specify an attribute of account\nentity, which will be used to store current account balance. This can be done via `\\Illuminatech\\Balance\\Balance::$accountBalanceAttribute`.\nEach time balance manager performs a transaction, it will update this attribute accordingly:\n\n```php\n\u003c?php\n\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminatech\\Balance\\Facades\\Balance;\n\nBalance::transfer($fromAccountId, $toAccountId, 100); // assume this is first time accounts are affected\n\n$currentBalance = DB::table('balance_accounts')\n    -\u003eselect(['balance'])\n    -\u003ewhere(['id' =\u003e $fromAccountId])\n    -\u003evalue('balance');\n\necho $currentBalance; // outputs: -100\n```\n\n\n## Saving extra transaction data \u003cspan id=\"saving-extra-transaction-data\"\u003e\u003c/span\u003e\n\nUsually there is a necessity to save extra information along with the transaction. For example: we may need to save\npayment ID received from payment gateway. This can be achieved in following way:\n\n```php\n\u003c?php\n\nuse Illuminatech\\Balance\\Facades\\Balance;\n\n$user = request()-\u003euser();\n\n// simple increase :\nBalance::increase(\n    [\n        'userId' =\u003e $user-\u003eid,\n        'type' =\u003e 'virtual-money',\n    ],\n    100,\n    // extra data associated with transaction :\n    [\n        'paymentGateway' =\u003e 'PayPal',\n        'paymentId' =\u003e 'abcxyzerft',\n    ]\n);\n\n// transfer :\nBalance::transfer(\n    [\n        'userId' =\u003e $user-\u003eid,\n        'type' =\u003e 'payment-gateway',\n    ],\n    [\n        'userId' =\u003e $user-\u003eid,\n        'type' =\u003e 'virtual-money',\n    ],\n    100,\n    // extra data associated with transaction :\n    [\n        'paymentGateway' =\u003e 'PayPal',\n        'paymentId' =\u003e 'abcxyzerft',\n    ]\n);\n```\n\nThe way extra attributes are stored in the data storage depends on particular balance manager implementation.\nFor example: `\\Illuminatech\\Balance\\BalanceDb` will try to store extra data inside transaction table columns, if their name\nequals the parameter name. You may as well setup special data field via `\\Illuminatech\\Balance\\BalanceDb::$dataAttribute`,\nwhich will store all extra parameters, which have no matching column, in serialized state.\n\n\u003e Note: watch for the keys you use in transaction data: make sure they do not conflict with columns, which are\n  reserved for other purposes, like primary keys.\n\n\n## Saving balance amount per transaction \u003cspan id=\"saving-balance-amount-per-transaction\"\u003e\u003c/span\u003e\n\nThere is a common accounting (bookkeeping) practice to record new balance amount per each performed transaction.\nSuch approach simplifies recreation of the balance transfers dynamics and search for possible errors.\nYou can achieve such behavior by setting `\\Illuminatech\\Balance\\Balance::$newBalanceAttribute` value with the name of\ntransaction entity attribute, which should store account balance, which appears after this transaction has been performed.\nFor example:\n\n```php\n\u003c?php\n\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminatech\\Balance\\Facades\\Balance;\n\n$accountId = 1;\n\n$lastTransactionQuery = DB::table('balance_transactions')\n    -\u003ewhere(['account_id' =\u003e $accountId])\n    -\u003eorderBy('id', 'DESC');\n\nBalance::increase($accountId, 50); // assume this is first time accounts is affected\n$lastTransaction = $lastTransactionQuery-\u003efirst();\necho $lastTransaction-\u003enew_balance; // outputs: 50\n\nBalance::increase($accountId, 25);\n$lastTransaction = $lastTransactionQuery-\u003efirst();\necho $lastTransaction-\u003enew_balance; // outputs: 75\n\nBalance::decrease($accountId, 50);\n$lastTransaction = $lastTransactionQuery-\u003efirst();\necho $lastTransaction-\u003enew_balance; // outputs: 25\n```\n\n\n## Events \u003cspan id=\"events\"\u003e\u003c/span\u003e\n\n`\\Illuminatech\\Balance\\Balance` provides several events, which can be handled via event listener:\n\n - [\\Illuminatech\\Balance\\Events\\CreatingTransaction](src/Events/CreatingTransaction.php) - raised before creating new transaction.\n - [\\Illuminatech\\Balance\\Events\\TransactionCreated](src/Events/TransactionCreated.php) - raised after creating new transaction.\n\nFor example:\n\n```php\n\u003c?php\n\nuse Illuminate\\Support\\Facades\\Event;\nuse Illuminatech\\Balance\\Facades\\Balance;\nuse Illuminatech\\Balance\\Events\\TransactionCreated;\nuse Illuminatech\\Balance\\Events\\CreatingTransaction;\n\nEvent::listen(CreatingTransaction::class, function (CreatingTransaction $event) {\n    $event-\u003edata['amount'] += 10; // you may adjust transaction data to be saved, including transaction amount\n    $event-\u003edata['comment'] = 'adjusted by event handler';\n});\n\nEvent::listen(TransactionCreated::class, function (TransactionCreated $event) {\n    echo 'new transaction: '.$event-\u003etransactionId; // you may get newly created transaction ID\n});\n\nBalance::increase(1, 100); // outputs: 'new transaction: 1'\necho Balance::calculateBalance(1); // outputs: 110\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filluminatech%2Fbalance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filluminatech%2Fbalance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filluminatech%2Fbalance/lists"}