yii2tech/balance

此包已被废弃,不再维护。未建议替代包。

提供基于借方和贷方原则的余额会计系统支持

赞助包维护!
klimov-paul
Patreon

安装次数: 38,690

依赖项: 0

建议者: 0

安全性: 0

星标: 167

关注者: 18

分支: 35

开放问题: 0

类型:yii2-extension

1.0.3 2018-09-19 10:14 UTC

This package is auto-updated.

Last update: 2022-01-10 10:31:50 UTC


README

12951949

为Yii2的余额会计系统扩展


此扩展提供了基于借方和贷方原则的余额会计系统的基本支持。

有关许可证信息,请参阅LICENSE文件。

Latest Stable Version Total Downloads Build Status

安装

安装此扩展的首选方式是通过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