yii2tech / balance
提供基于借方和贷方原则的余额会计系统支持
Requires
- yiisoft/yii2: ~2.0.14
This package is auto-updated.
Last update: 2022-01-10 10:31:50 UTC
README
为Yii2的余额会计系统扩展
此扩展提供了基于借方和贷方原则的余额会计系统的基本支持。
有关许可证信息,请参阅LICENSE文件。
安装
安装此扩展的首选方式是通过composer。
运行以下命令:
php composer.phar require --prefer-dist yii2tech/balance
或添加以下内容到您的composer.json文件的require部分:
"yii2tech/balance": "*"
使用
此扩展提供了基于借方和贷方原则的余额会计系统的基本支持。余额系统通常用于会计(簿记)和货币操作。然而,它也可以用于任何资源从一个位置转移到另一个位置。例如:将商品从仓库转移到商店等。
与余额系统相关的两个主要术语
- 账户 - 具有某种逻辑意义的资源虚拟存储。
- 交易 - 表示资源到或从特定账户的实际转移。
假设我们有一个为用户提供虚拟货币余额的系统。余额中的货币可以用于购买商品,用户可以通过某些支付网关为余额充值。在这个例子中,每个用户都应该有3个虚拟余额账户:'virtual-money'、'payment-gateway'和'purchases'。当用户为虚拟余额充值时,我们的系统应从'payment-gateway'中扣除资金并添加到'virtual-money'中。当用户购买商品时,我们的系统应从'virtual-money'中扣除资金并添加到'purchases'中。技巧是:如果你将所有与用户相关的账户('payment-gateway' + 'virtual-money' + 'purchases')的当前金额加起来,它总是等于零。这种检查可以在任何时候验证是否出现了错误。
此扩展引入了术语“余额管理器”作为Yii应用程序组件,它应该处理所有余额交易。提供了此类组件的几个实现
- [[yii2tech\balance\ManagerDb]] - 使用关系型数据库作为数据存储。
- [[yii2tech\balance\ManagerMongoDb]] - 使用MongoDB作为数据存储。
- [[yii2tech\balance\ManagerActiveRecord]] - 使用ActiveRecord类进行数据存储。
请参阅特定管理器类以获取更多详细信息。
您可以使用余额管理器作为独立对象或将其配置为应用程序组件。应用程序配置示例
return [ 'components' => [ 'balanceManager' => [ 'class' => 'yii2tech\balance\ManagerDb', 'accountTable' => '{{%BalanceAccount}}', 'transactionTable' => '{{%BalanceTransaction}}', 'accountLinkAttribute' => 'accountId', 'amountAttribute' => 'amount', 'dataAttribute' => 'data', ], ], ... ];
为了增加特定账户的余额(借方),使用[[\yii2tech\balance\ManagerInterface::increase()]]方法
Yii::$app->balanceManager->increase($accountId, 500); // add 500 credits to account
为了减少特定账户的余额(贷方),使用[[\yii2tech\balance\ManagerInterface::decrease()]]方法
Yii::$app->balanceManager->decrease($accountId, 100); // remove 100 credits from account
提示:实际上,方法
decrease()
是多余的,你可以通过调用increase()
并传入负数来达到相同的结果。
在您的应用程序中,很可能不会直接使用 increase()
和 decrease()
方法。在大多数情况下,需要一次性将资金从一个账户转移到另一个账户。可以使用 [[\yii2tech\balance\ManagerInterface::transfer()]] 方法进行此操作
$fromId = 1; $toId = 2; Yii::$app->balanceManager->transfer($fromId, $to, 100); // remove 100 credits from account 1 and add 100 credits to account 2
请注意,方法 transfer()
会创建两个独立的交易:每个受影响的账户一个。因此,您可以轻松地检索特定账户的所有资金转移历史,只需选择与该账户相关联的所有交易即可。'借方'交易将具有正金额,而'贷方'交易将具有负金额。
注意:如果您希望由
transfer()
创建的交易记住过程中涉及的另一个账户,则需要设置 [[\yii2tech\balance\Manager::$extraAccountLinkAttribute]]。
您可以使用 [[\yii2tech\balance\ManagerInterface::revert()]] 方法撤消特定交易
Yii::$app->balanceManager->revert($transactionId);
此方法不会删除原始交易,而是创建一个新的交易来补偿它。
查询账户
对于余额管理器来说,使用账户 ID 并不是很实用。在我们上面的示例中,每个系统用户都有 3 个虚拟账户,每个账户都有一个唯一的 ID。然而,在执行购买时,我们使用用户 ID 和账户类型,因此在使用余额管理器之前需要查询实际的账户 ID。因此,可以指定账户的属性集来指定用于余额管理器方法的账户。例如
Yii::$app->balanceManager->transfer( [ 'userId' => Yii::$app->user->id, 'type' => 'virtual-money', ], [ 'userId' => Yii::$app->user->id, 'type' => 'purchases', ], 500 );
在此示例中,余额管理器将自动找到受影响账户的 ID,使用提供的属性作为过滤器。
您可以通过启用 [[yii2tech\balance\Manager::$autoCreateAccount]],允许自动创建缺失的账户,如果它们指定为属性集,从而启用此功能。这允许根据需求动态创建账户,消除预先创建账户的必要性。
请注意!实际上,'账户'实体在余额系统中是多余的,并且其使用可以避免。然而,它的存在提供了更多的灵活性并节省了性能。在此扩展中存储账户数据不是强制性的,您可以根据需要配置您的余额管理器,使其不使用。
查找账户当前余额
特定账户的当前金额始终可以计算为相关交易金额的总和。您可以使用 [[\yii2tech\balance\ManagerInterface::calculateBalance()]] 方法进行此操作
Yii::$app->balanceManager->transfer($fromAccount, $toAccount, 100); // assume this is first time accounts are affected echo Yii::$app->balanceManager->calculateBalance($fromAccount); // outputs: -100 echo Yii::$app->balanceManager->calculateBalance($toAccount); // outputs: 100
但是,每次需要时都计算当前余额并不高效。因此,您可以指定账户实体的一个属性,该属性将用于存储当前账户余额。这可以通过 [[\yii2tech\balance\Manager::$accountBalanceAttribute]] 来完成。每次余额管理器执行交易时,它都会相应地更新此属性。
use yii\db\Query; Yii::$app->balanceManager->transfer($fromAccountId, $toAccountId, 100); // assume this is first time accounts are affected $currentBalance = (new Query()) ->select(['balance']) ->from('BalanceAccount') ->andWhere(['id' => $fromAccountId]) ->scalar(); echo $currentBalance; // outputs: -100
保存额外交易数据
通常需要将额外信息与交易一起保存。例如:我们可能需要保存从支付网关接收到的支付 ID。可以通过以下方式实现
// simple increase : Yii::$app->balanceManager->increase( [ 'userId' => Yii::$app->user->id, 'type' => 'virtual-money', ], 100, // extra data associated with transaction : [ 'paymentGateway' => 'PayPal', 'paymentId' => 'abcxyzerft', ] ); // transfer : Yii::$app->balanceManager->transfer( [ 'userId' => Yii::$app->user->id, 'type' => 'payment-gateway', ], [ 'userId' => Yii::$app->user->id, 'type' => 'virtual-money', ], 100, // extra data associated with transaction : [ 'paymentGateway' => 'PayPal', 'paymentId' => 'abcxyzerft', ] );
额外属性在数据存储中的存储方式取决于特定的余额管理器实现。例如:[[\yii2tech\balance\ManagerDb]] 将尝试将额外数据存储在交易表列中,如果它们的名称等于参数名称。您还可以通过 [[\yii2tech\balance\ManagerDb::$dataAttribute]] 设置特殊数据字段,该字段将所有没有匹配列的额外参数以序列化状态存储。
注意:注意您在交易数据中使用的键:确保它们不会与其他保留用于其他目的的列(如主键)冲突。
事件
[[\yii2tech\balance\Manager]] 提供了几个事件,可以通过事件处理器或行为来处理。
- [[yii2tech\balance\Manager::EVENT_BEFORE_CREATE_TRANSACTION]] - 在创建新交易前触发。
- [[yii2tech\balance\Manager::EVENT_AFTER_CREATE_TRANSACTION]] - 在创建新交易后触发。
例如
use yii2tech\balance\Manager; use yii2tech\balance\ManagerDb; $manager = new ManagerDb(); $manager->on(Manager::EVENT_BEFORE_CREATE_TRANSACTION, function ($event) { $event->transactionData['amount'] += 10; // you may adjust transaction data to be saved, including transaction amount $event->transactionData['comment'] = 'adjusted by event handler'; }); $manager->on(Manager::EVENT_AFTER_CREATE_TRANSACTION, function ($event) { echo 'new transaction: ' $event->transactionId; // you may get newly created transaction ID }); $manager->increase(1, 100); // outputs: 'new transaction: 1' echo Yii::$app->balanceManager->calculateBalance(1); // outputs: 110