CaiWuXiTong/tests/Feature/Business/FinanceLedgerHeaderControlsTest.php
shanshi 1761de6ee3 feat: 财务台账默认展示账户资金汇总
默认按账户汇总所选期间的期初、收入、支出和期末余额,保留按日流水视图。
2026-11-07 17:49:52 +08:00

617 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Tests\Feature\FeatureTestCase;
class FinanceLedgerHeaderControlsTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow('2026-10-18 12:00:00');
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
[
'company_name' => '甲公司',
'account_short' => '账户甲',
'account_type' => '公户',
'region' => '上海',
'company_plan' => '保留',
'account_should_keep' => '是',
'current_balance' => 100,
'daily_available' => 80,
'blocked_amount' => 20,
],
[
'company_name' => '乙公司',
'account_short' => '账户乙',
'account_type' => '支付宝',
'region' => '杭州',
'company_plan' => '待注销',
'account_should_keep' => '否',
'current_balance' => 300,
'daily_available' => 300,
'blocked_amount' => 0,
],
]);
$connection->table('fin_shops')->insert([
[
'shop_name' => '天猫店铺',
'platform' => '天猫',
'company_name' => '甲公司',
'account_short' => '账户甲',
'status' => '在营',
'shop_balance' => 100,
],
[
'shop_name' => '拼多多店铺',
'platform' => '拼多多',
'company_name' => '乙公司',
'account_short' => '账户乙',
'status' => '停业',
'shop_balance' => 300,
],
]);
$connection->table('fin_daily_flows')->insert([
[
'flow_date' => '2026-10-17',
'company_name' => '甲公司',
'account_short' => '账户甲',
'opening_balance' => 100,
'inflow' => 10,
'outflow' => 0,
'closing_balance' => 110,
],
[
'flow_date' => '2026-10-18',
'company_name' => '乙公司',
'account_short' => '账户乙',
'opening_balance' => 300,
'inflow' => 0,
'outflow' => 20,
'closing_balance' => 280,
],
]);
$connection->table('fin_transactions')->insert([
[
'txn_date' => '2026-10-17',
'account_short' => '账户甲',
'detail' => '甲笔流水',
'income' => 10,
'expense' => 0,
'approval_no' => 'APR-甲',
'source' => 'dingtalk',
'source_value' => 'VAL-甲',
'process_type' => '报销',
'row_key' => 'header-filter-row-a',
],
[
'txn_date' => '2026-10-18',
'account_short' => '账户乙',
'detail' => '乙笔流水',
'income' => 0,
'expense' => 20,
'approval_no' => 'APR-乙',
'source' => 'shop',
'source_value' => 'VAL-乙',
'process_type' => '提现',
'row_key' => 'header-filter-row-b',
],
]);
$connection->table('fin_account_map')->insert([
[
'source' => 'dingtalk',
'source_value' => 'VAL-甲',
'account_short' => '账户甲',
'txn_count' => 1,
'income_sum' => 10,
'expense_sum' => 0,
],
[
'source' => 'shop',
'source_value' => 'VAL-乙',
'account_short' => null,
'txn_count' => 1,
'income_sum' => 0,
'expense_sum' => 20,
],
]);
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function test_every_financial_ledger_table_has_a_header_menu(): void
{
$response = $this->loginAs()->get($this->pageUrl());
$html = $response->assertOk()->getContent();
$this->assertSame(71, preg_match_all('/<details\b[^>]*data-ledger-header-details/', $html));
$this->assertSame(71, preg_match_all('/<span\b[^>]*data-ledger-header-label/', $html));
$this->assertSame(2, preg_match_all('/<select name="transactions_filter_value"/', $html));
$this->assertSame(1, preg_match_all('/<input\b[^>]*name="transactions_filter_value"[^>]*list="ledger-header-options-transactions-effective_account_short"/', $html));
$this->assertStringContainsString(
'<datalist id="ledger-header-options-transactions-effective_account_short">',
$html
);
$this->assertStringContainsString('<option value="账户甲"', $html);
$this->assertStringContainsString('<option value="2026-10-18"', $html);
$this->assertStringContainsString('data-transaction-column-storage-key', $html);
foreach (['accounts', 'shops', 'flows', 'transactions', 'mapping', 'sources'] as $tab) {
$this->assertStringContainsString('name="' . $tab . '_filter_value"', $html);
$this->assertStringContainsString($tab . '_sort=', $html);
}
}
public function test_financial_ledger_date_filters_use_dual_month_range_controls(): void
{
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$this->assertSame(4, preg_match_all('/<div class="ledger-date-range" data-ledger-date-range>/', $html));
$this->assertSame(4, preg_match_all('/>查询时间段<\/label>/', $html));
$this->assertSame(4, preg_match_all('/<button\b[^>]*data-ledger-date-range-trigger/', $html));
$this->assertSame(4, preg_match_all('/<input\b[^>]*data-ledger-date-range-start/', $html));
$this->assertSame(4, preg_match_all('/<input\b[^>]*data-ledger-date-range-end/', $html));
$this->assertSame(4, preg_match_all('/<div class="ledger-date-range__popover"/', $html));
$this->assertSame(8, preg_match_all('/<div class="ledger-date-range__days" data-ledger-date-range-days>/', $html));
$this->assertSame(8, preg_match_all('/<strong data-ledger-date-range-title><\/strong>/', $html));
$this->assertSame(8, preg_match_all('/<button\b[^>]*data-ledger-date-range-shift="/', $html));
foreach ([
'flows' => ['flows_from', 'flows_to'],
'txns' => ['txns_from', 'txns_to'],
'mapping' => ['mapping_from', 'mapping_to'],
'sources' => ['sources_from', 'sources_to'],
] as $prefix => [$fromName, $toName]) {
$this->assertStringContainsString('name="' . $fromName . '"', $html);
$this->assertStringContainsString('name="' . $toName . '"', $html);
$this->assertStringNotContainsString('id="' . $prefix . '-from" type="date"', $html);
$this->assertStringNotContainsString('id="' . $prefix . '-to" type="date"', $html);
}
}
public function test_transaction_summary_uses_previous_month_close_and_current_month_cash_flow(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_daily_flows')->insert([
'flow_date' => '2026-09-30',
'company_name' => '甲公司',
'account_short' => '账户甲',
'opening_balance' => 80000,
'inflow' => 10000,
'outflow' => 0,
'closing_balance' => 90000,
]);
$connection->table('fin_transactions')->insert([
'txn_date' => '2026-10-20',
'account_short' => '账户甲',
'detail' => '当月支出',
'income' => 0,
'expense' => 5000,
'approval_no' => 'APR-支出',
'source' => 'manual',
'source_value' => null,
'process_type' => '手工录入',
'row_key' => 'period-summary-expense',
]);
$connection->table('fin_accounts')
->where('account_short', '账户甲')
->update(['current_balance' => 185000]);
$connection->table('fin_transactions')
->where('row_key', 'header-filter-row-a')
->update(['income' => 100000]);
$html = $this->loginAs()->get($this->pageUrl([
'txns_account' => '账户甲',
'txns_from' => '2026-10-01',
'txns_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertSame(1, preg_match('/(<div class="ledger-summary" data-ledger-transaction-summary>.*?)(?=<div class="ledger-note">)/s', $html, $matches));
$summaryHtml = $matches[1];
$this->assertStringContainsString('>上月期末余额(万元)</span><b title="90,000.00 元" data-ledger-transaction-summary-previous-closing>9.00</b>', $summaryHtml);
$this->assertStringContainsString('>当月收入(万元)</span><b title="100,000.00 元" data-ledger-transaction-summary-income>10.00</b>', $summaryHtml);
$this->assertStringContainsString('>当月支出(万元)</span><b title="5,000.00 元" data-ledger-transaction-summary-expense>0.50</b>', $summaryHtml);
$this->assertStringContainsString('>当前余额(万元)</span><b title="185,000.00 元" data-ledger-transaction-summary-current-balance>18.50</b>', $summaryHtml);
$this->assertStringNotContainsString('data-ledger-transaction-summary-count', $html);
$this->assertStringNotContainsString('data-ledger-transaction-summary-net', $html);
}
public function test_independent_statement_summary_uses_only_its_own_monthly_balances(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '朱俊祥',
'account_short' => '朱俊祥支付宝',
'account_type' => '支付宝',
'current_balance' => 999999,
]);
$connection->table('fin_transactions')->insert([
[
'txn_date' => '2026-10-01',
'account_short' => '朱俊祥支付宝',
'detail' => '独立账单首笔',
'income' => 10,
'expense' => 0,
'balance' => 110,
'approval_no' => 'INDEPENDENT-INCOME',
'source' => 'alipay-company',
'source_value' => 'ALI-INDEPENDENT',
'row_key' => 'independent-summary-income',
],
[
'txn_date' => '2026-10-02',
'account_short' => '朱俊祥支付宝',
'detail' => '独立账单末笔',
'income' => 0,
'expense' => 20,
'balance' => 90,
'approval_no' => 'INDEPENDENT-EXPENSE',
'source' => 'alipay-company',
'source_value' => 'ALI-INDEPENDENT',
'row_key' => 'independent-summary-expense',
],
]);
$html = $this->loginAs()->get($this->pageUrl([
'txns_view' => 'zhu-junxiang-alipay',
'txns_from' => '2026-10-01',
'txns_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertSame(1, preg_match('/(<div class="ledger-summary" data-ledger-transaction-summary>.*?)(?=<div class="ledger-note">)/s', $html, $matches));
$summaryHtml = $matches[1];
$this->assertStringContainsString('>上月期末余额(万元)</span><b title="100.00 元" data-ledger-transaction-summary-previous-closing>0.01</b>', $summaryHtml);
$this->assertStringContainsString('>当月收入(万元)</span><b title="10.00 元" data-ledger-transaction-summary-income>0.00</b>', $summaryHtml);
$this->assertStringContainsString('>当月支出(万元)</span><b title="20.00 元" data-ledger-transaction-summary-expense>0.00</b>', $summaryHtml);
$this->assertStringContainsString('>当前余额(万元)</span><b title="90.00 元" data-ledger-transaction-summary-current-balance>0.01</b>', $summaryHtml);
$this->assertStringContainsString('独立账单页', $html);
$this->assertSame(1, preg_match('/<input id="ledger-manual-date"[^>]*>/', $html, $dateInput));
$this->assertStringNotContainsString('min=', $dateInput[0]);
$allHtml = $this->loginAs()->get($this->pageUrl([
'txns_from' => '2026-10-01',
'txns_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertStringNotContainsString('独立账单首笔', $allHtml);
$this->assertStringNotContainsString('独立账单末笔', $allHtml);
$this->assertSame(1, preg_match('/<input id="ledger-manual-date"[^>]*>/', $allHtml, $allDateInput));
$this->assertStringContainsString('min="2026-10-01"', $allDateInput[0]);
$nextMonthHtml = $this->loginAs()->get($this->pageUrl([
'txns_view' => 'zhu-junxiang-alipay',
'txns_from' => '2026-11-01',
'txns_to' => '2026-11-30',
]))->assertOk()->getContent();
$this->assertSame(1, preg_match('/(<div class="ledger-summary" data-ledger-transaction-summary>.*?)(?=<div class="ledger-note">)/s', $nextMonthHtml, $nextMatches));
$this->assertStringContainsString('title="90.00 元" data-ledger-transaction-summary-previous-closing>0.01</b>', $nextMatches[1]);
$this->assertStringContainsString('title="90.00 元" data-ledger-transaction-summary-current-balance>0.01</b>', $nextMatches[1]);
$this->assertStringContainsString('data-ledger-transaction-summary-income>0.00</b>', $nextMatches[1]);
$this->assertStringContainsString('data-ledger-transaction-summary-expense>0.00</b>', $nextMatches[1]);
}
public function test_independent_statement_accounts_are_hidden_from_shared_ledger_views(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '朱俊祥',
'account_short' => '朱俊祥支付宝',
'account_type' => '支付宝',
'current_balance' => 999999,
'daily_available' => 999999,
]);
$connection->table('fin_transactions')->insert([
'txn_date' => '2026-10-18',
'account_short' => '朱俊祥支付宝',
'detail' => '共享明细不应出现',
'income' => 10,
'expense' => 0,
'balance' => 100,
'approval_no' => 'INDEPENDENT-HIDDEN',
'source' => 'alipay-company',
'source_value' => 'ALI-INDEPENDENT',
'row_key' => 'independent-hidden-row',
]);
$html = $this->loginAs()->get($this->pageUrl() . '?tab=accounts')->assertOk()->getContent();
$this->assertSame(1, preg_match('/<table class="ledger-table ledger-accounts-table">.*?<\/table>/s', $html, $matches));
$this->assertStringNotContainsString('朱俊祥支付宝', $matches[0]);
$this->assertStringNotContainsString('共享明细不应出现', $html);
$this->assertStringNotContainsString('999999.00', $html);
}
public function test_shop_ledger_header_labels_and_column_order(): void
{
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$panelStart = strpos($html, 'id="ledger-panel-shops"');
$theadStart = strpos($html, '<thead>', $panelStart);
$theadEnd = strpos($html, '</thead>', $theadStart);
$this->assertNotFalse($panelStart);
$this->assertNotFalse($theadStart);
$this->assertNotFalse($theadEnd);
$headerHtml = substr($html, $theadStart, $theadEnd - $theadStart);
$platformPosition = strpos($headerHtml, 'data-ledger-header-field="platform"');
$shopNamePosition = strpos($headerHtml, 'data-ledger-header-field="shop_name"');
$this->assertNotFalse($platformPosition);
$this->assertNotFalse($shopNamePosition);
$this->assertLessThan($shopNamePosition, $platformPosition);
$this->assertStringContainsString('data-ledger-header-label>平台</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>ERP系统店铺名称</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>店铺主体</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>店铺负责人</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>部门负责人</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>余额来源</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>预留标准</span>', $headerHtml);
$this->assertStringContainsString('data-ledger-header-label>余额预警</span>', $headerHtml);
$this->assertStringNotContainsString('data-ledger-header-label>店铺名称</span>', $headerHtml);
$this->assertStringNotContainsString('data-ledger-header-label>所属公司</span>', $headerHtml);
}
public function test_transaction_account_filter_provides_typeahead_suggestions(): void
{
$html = $this->loginAs()->get($this->pageUrl())
->assertOk()
->assertSee('data-ledger-account-filter', false)
->assertSee('data-limit="60"', false)
->assertSee('id="txns-account"', false)
->assertSee('role="combobox"', false)
->assertSee('aria-autocomplete="list"', false)
->assertSee('aria-controls="txns-account-menu"', false)
->assertSee('aria-haspopup="listbox"', false)
->assertSee('data-ledger-account-filter-toggle', false)
->assertSee('data-ledger-account-filter-menu', false)
->assertSee('max-height:min(18rem, 50vh)', false)
->assertSee("input.addEventListener('input'", false)
->assertDontSee('id="txns-account" name="txns_account" list=', false)
->getContent();
$this->assertStringContainsString('<option value="账户甲"', $html);
$this->assertStringContainsString('<option value="账户乙"', $html);
}
public function test_account_and_flow_headers_filter_and_sort_the_full_dataset(): void
{
$filtered = $this->loginAs()->get($this->pageUrl([
'accounts_filter_column' => 'company_name',
'accounts_filter_value' => '乙公司',
]))->assertOk();
$filtered->assertSee('data-account-short="账户乙"', false)
->assertDontSee('data-account-short="账户甲"', false);
$sortedAccounts = $this->loginAs()->get($this->pageUrl([
'accounts_sort' => 'current_balance',
'accounts_direction' => 'desc',
]))->assertOk()->getContent();
$this->assertLessThan(
strpos($sortedAccounts, 'data-account-short="账户甲"'),
strpos($sortedAccounts, 'data-account-short="账户乙"'),
);
$flows = $this->loginAs()->get($this->pageUrl([
'accounts_filter_column' => 'company_name',
'accounts_filter_value' => '乙公司',
'shops_filter_column' => 'company_name',
'shops_filter_value' => '乙公司',
'flows_filter_column' => 'account_short',
'flows_filter_value' => '账户乙',
]))->assertOk();
$flows->assertSee('>乙公司</td>', false)
->assertDontSee('>甲公司</td>', false);
}
public function test_account_flow_summary_defaults_to_one_row_per_account_and_keeps_daily_view_available(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '丙公司',
'account_short' => '账户丙',
'account_type' => '公户',
'current_balance' => 0,
'daily_available' => 0,
'blocked_amount' => 0,
]);
$summaryHtml = $this->loginAs()->get($this->pageUrl([
'flows_from' => '2026-10-01',
'flows_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertSame(1, preg_match('/<tr data-flow-summary-account="账户甲">.*?<\/tr>/s', $summaryHtml, $accountRow));
$this->assertStringContainsString('<td>账户甲</td><td>甲公司</td>', $accountRow[0]);
$this->assertStringContainsString('<td class="num">100.00</td><td class="num">10.00</td><td class="num"></td><td class="num">110.00</td><td class="num">1</td>', $accountRow[0]);
$this->assertSame(1, preg_match('/<tr data-flow-summary-account="账户丙">.*?<\/tr>/s', $summaryHtml, $emptyAccountRow));
$this->assertStringContainsString('<td class="num">—</td>', $emptyAccountRow[0]);
$this->assertStringContainsString('<td>尚未起账</td>', $emptyAccountRow[0]);
$this->assertStringContainsString('>账户汇总</a>', $summaryHtml);
$this->assertStringContainsString('>按日流水</a>', $summaryHtml);
$dailyHtml = $this->loginAs()->get($this->pageUrl([
'flows_view' => 'daily',
'flows_from' => '2026-10-01',
'flows_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertStringContainsString('>当日入账</span>', $dailyHtml);
$this->assertStringContainsString('>2026-10-17</td>', $dailyHtml);
}
public function test_shop_transaction_mapping_and_source_headers_filter_their_tables(): void
{
$this->loginAs()->get($this->pageUrl([
'shops_filter_column' => 'platform',
'shops_filter_value' => '天猫',
]))
->assertOk()
->assertSee('>天猫店铺</td>', false)
->assertDontSee('>拼多多店铺</td>', false);
$this->loginAs()->get($this->pageUrl([
'transactions_filter_column' => 'approval_no',
'transactions_filter_value' => 'APR-乙',
'transactions_sort' => 'expense',
'transactions_direction' => 'desc',
]))
->assertOk()
->assertSee('APR-乙')
->assertDontSee('APR-甲');
$this->loginAs()->get($this->pageUrl([
'transactions_filter_column' => 'effective_account_short',
'transactions_filter_value' => '乙',
]))
->assertOk()
->assertSee('乙笔流水')
->assertDontSee('甲笔流水');
$this->loginAs()->get($this->pageUrl([
'mapping_filter_column' => 'mapping_status',
'mapping_filter_value' => 'unmapped',
]))
->assertOk()
->assertSee('VAL-乙')
->assertDontSee('VAL-甲');
$this->loginAs()->get($this->pageUrl([
'sources_filter_column' => 'source',
'sources_filter_value' => 'shop',
]))
->assertOk()
->assertSee('>提现</td>', false)
->assertDontSee('>报销</td>', false);
}
public function test_ledger_pages_default_to_the_current_month_and_allow_history_selection(): void
{
Carbon::setTestNow('2026-11-05 12:00:00');
$connection = DB::connection('business_mirror');
$connection->table('fin_daily_flows')->insert([
'flow_date' => '2026-11-02',
'company_name' => '甲公司',
'account_short' => '账户甲',
'opening_balance' => 100,
'inflow' => 30,
'outflow' => 0,
'closing_balance' => 130,
'remark' => '11月流水行',
]);
$connection->table('fin_transactions')->insert([
'txn_date' => '2026-11-02',
'account_short' => '账户甲',
'detail' => '11月动账明细',
'income' => 30,
'expense' => 0,
'approval_no' => 'APR-11',
'source' => 'dingtalk',
'source_value' => 'VAL-11',
'process_type' => '11月报销',
'row_key' => 'current-month-row-11',
]);
$connection->table('fin_account_map')->insert([
'source' => 'dingtalk',
'source_value' => 'VAL-11',
'account_short' => '账户甲',
'txn_count' => 1,
'income_sum' => 30,
'expense_sum' => 0,
]);
$this->loginAs()->get($this->pageUrl(['flows_view' => 'daily']))
->assertOk()
->assertSee('name="flows_from" value="2026-11-01"', false)
->assertSee('name="flows_to" value="2026-11-30"', false)
->assertSee('name="txns_from" value="2026-11-01"', false)
->assertSee('name="txns_to" value="2026-11-30"', false)
->assertSee('name="mapping_from" value="2026-11-01"', false)
->assertSee('name="mapping_to" value="2026-11-30"', false)
->assertSee('name="sources_from" value="2026-11-01"', false)
->assertSee('name="sources_to" value="2026-11-30"', false)
->assertSee('11月流水行')
->assertSee('11月动账明细')
->assertSee('VAL-11')
->assertSee('11月报销')
->assertDontSee('甲笔流水')
->assertDontSee('VAL-甲')
->assertDontSee('>报销</td>', false);
$this->loginAs()->get($this->pageUrl([
'flows_view' => 'daily',
'flows_from' => '2026-10-01',
'flows_to' => '2026-10-31',
'txns_from' => '2026-10-01',
'txns_to' => '2026-10-31',
'mapping_from' => '2026-10-01',
'mapping_to' => '2026-10-31',
'sources_from' => '2026-10-01',
'sources_to' => '2026-10-31',
]))
->assertOk()
->assertSee('甲笔流水')
->assertSee('VAL-甲')
->assertSee('>报销</td>', false)
->assertDontSee('11月动账明细')
->assertDontSee('VAL-11');
}
public function test_flow_summary_uses_period_opening_and_closing_instead_of_summing_daily_balances(): void
{
$connection = DB::connection('business_mirror');
$connection->table('fin_daily_flows')->insert([
'flow_date' => '2026-10-19',
'company_name' => '甲公司',
'account_short' => '账户甲',
'opening_balance' => 110,
'inflow' => 50000,
'outflow' => 0,
'closing_balance' => 50110,
]);
$html = $this->loginAs()->get($this->pageUrl([
'flows_from' => '2026-10-01',
'flows_to' => '2026-10-31',
]))->assertOk()->getContent();
$this->assertStringContainsString(
'>期初余额合计(万元)</span><b title="所选期间开始时的期初余额合计:400.00 元">0.04</b>',
$html,
);
$this->assertStringContainsString(
'>当期入账(万元)</span><b title="所选期间的入账金额:50,010.00 元">5.00</b>',
$html,
);
$this->assertStringContainsString(
'>当期出账(万元)</span><b title="所选期间的出账金额:20.00 元">0.00</b>',
$html,
);
$this->assertStringContainsString(
'>期末余额合计(万元)</span><b title="所选期间结束时的账户余额合计:50,390.00 元">5.04</b>',
$html,
);
}
private function pageUrl(array $parameters = []): string
{
return route('business.pages.show', array_merge([
'company_id' => $this->company->id,
'section' => 'financial-operations',
'page' => 'ledger',
], $parameters));
}
}