parent
b4783ae528
commit
45dade3c26
@ -16,6 +16,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
use Modules\EcommerceFinance\Services\FinanceLedgerAccountCreator;
|
||||
use Modules\EcommerceFinance\Services\FinanceLedgerAccountEditor;
|
||||
use Modules\EcommerceFinance\Services\FinanceLedgerAccountTemplate;
|
||||
@ -45,6 +46,26 @@ class FinanceLedger extends Controller
|
||||
return $template->download();
|
||||
}
|
||||
|
||||
public function accountBasicPage(FinanceLedgerPageData $pageData, string $view): View
|
||||
{
|
||||
$definition = FinanceLedgerPageData::ACCOUNT_TYPE_VIEWS[$view] ?? null;
|
||||
abort_if($definition === null, 404);
|
||||
|
||||
$ledgerData = $pageData->page('accounts', $view);
|
||||
$ledgerData['ledgerStandaloneAccountView'] = $view;
|
||||
$ledgerData['ledgerStandaloneAccountTitle'] = (string) $definition['title'];
|
||||
$ledgerData['ledgerStandaloneAccountDescription'] = (string) $definition['description'];
|
||||
$ledgerData['ledgerStandaloneAccountBackUrl'] = route('business.pages.show', [
|
||||
'section' => 'financial-operations',
|
||||
'page' => 'ledger',
|
||||
]) . '?tab=flows&flows_view=basic';
|
||||
|
||||
return view('business.pages.financial-ledger.account-basic-page', [
|
||||
'ledgerData' => $ledgerData,
|
||||
'pageTitle' => (string) $definition['title'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeAccount(Request $request, FinanceLedgerAccountCreator $creator): JsonResponse
|
||||
{
|
||||
abort_unless(
|
||||
|
||||
@ -37,6 +37,34 @@ class FinanceLedgerPageData
|
||||
'sources' => ['title' => '数据来源', 'icon' => 'database'],
|
||||
];
|
||||
|
||||
public const ACCOUNT_TYPE_VIEWS = [
|
||||
'bank' => [
|
||||
'title' => '银行账户',
|
||||
'description' => '银行开户、网点和限额等基本资料',
|
||||
'icon' => 'account_balance',
|
||||
],
|
||||
'non_store' => [
|
||||
'title' => '支付宝',
|
||||
'description' => '支付宝账号、商户和实名主体资料',
|
||||
'icon' => 'account_balance_wallet',
|
||||
],
|
||||
'wangshang' => [
|
||||
'title' => '网商银行',
|
||||
'description' => '网商银行账户及企业资料',
|
||||
'icon' => 'business_center',
|
||||
],
|
||||
'wallet' => [
|
||||
'title' => '网银钱包',
|
||||
'description' => '网银钱包账户及关联资料',
|
||||
'icon' => 'wallet',
|
||||
],
|
||||
'other' => [
|
||||
'title' => '其他账户',
|
||||
'description' => '尚未归入固定类型的账户资料',
|
||||
'icon' => 'category',
|
||||
],
|
||||
];
|
||||
|
||||
private const HIDDEN_TABS = [
|
||||
'accounts' => true,
|
||||
];
|
||||
@ -85,7 +113,7 @@ class FinanceLedgerPageData
|
||||
) {
|
||||
}
|
||||
|
||||
public function page(?string $tab = null): array
|
||||
public function page(?string $tab = null, ?string $accountView = null): array
|
||||
{
|
||||
$tab = (string) (request('tab') ?: $tab);
|
||||
$tab = array_key_exists($tab, self::TABS) ? $tab : 'flows';
|
||||
@ -95,7 +123,7 @@ class FinanceLedgerPageData
|
||||
$flowView = in_array($requestedFlowView, ['summary', 'bank', 'non_store', 'wangshang', 'wallet', 'daily', 'basic'], true)
|
||||
? $requestedFlowView
|
||||
: 'summary';
|
||||
$requestedAccountView = trim((string) request('accounts_view', 'all'));
|
||||
$requestedAccountView = trim((string) ($accountView ?? request('accounts_view', 'all')));
|
||||
$accountView = in_array($requestedAccountView, ['all', 'bank', 'non_store', 'wangshang', 'wallet', 'other'], true)
|
||||
? $requestedAccountView
|
||||
: 'all';
|
||||
@ -307,36 +335,9 @@ class FinanceLedgerPageData
|
||||
*/
|
||||
private function accountTypeCards(): array
|
||||
{
|
||||
$definitions = [
|
||||
'bank' => [
|
||||
'title' => '银行账户',
|
||||
'description' => '银行开户、网点和限额等基本资料',
|
||||
'icon' => 'account_balance',
|
||||
],
|
||||
'non_store' => [
|
||||
'title' => '支付宝',
|
||||
'description' => '支付宝账号、商户和实名主体资料',
|
||||
'icon' => 'account_balance_wallet',
|
||||
],
|
||||
'wangshang' => [
|
||||
'title' => '网商银行',
|
||||
'description' => '网商银行账户及企业资料',
|
||||
'icon' => 'business_center',
|
||||
],
|
||||
'wallet' => [
|
||||
'title' => '网银钱包',
|
||||
'description' => '网银钱包账户及关联资料',
|
||||
'icon' => 'wallet',
|
||||
],
|
||||
'other' => [
|
||||
'title' => '其他账户',
|
||||
'description' => '尚未归入固定类型的账户资料',
|
||||
'icon' => 'category',
|
||||
],
|
||||
];
|
||||
$cards = [];
|
||||
|
||||
foreach ($definitions as $view => $definition) {
|
||||
foreach (self::ACCOUNT_TYPE_VIEWS as $view => $definition) {
|
||||
$query = FinanceLedgerIndependentAccounts::exclude(
|
||||
$this->db()->table('fin_accounts as a'),
|
||||
'a.account_short',
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<x-layouts.admin>
|
||||
<x-slot name="title">账户基本信息 - {{ $pageTitle }}</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
@include($ledgerData['view'], $ledgerData)
|
||||
</x-slot>
|
||||
</x-layouts.admin>
|
||||
@ -14,6 +14,10 @@
|
||||
$accountPlanOptions = $ledgerAccountPlanOptions ?? [];
|
||||
$companyOptions = $ledgerCompanyOptions ?? [];
|
||||
$accountKeepOptions = $ledgerAccountKeepOptions ?? \App\Utilities\FinanceLedgerAccountShouldKeep::OPTIONS;
|
||||
$standaloneAccountView = $ledgerStandaloneAccountView ?? null;
|
||||
$standaloneAccountTitle = $ledgerStandaloneAccountTitle ?? '';
|
||||
$standaloneAccountDescription = $ledgerStandaloneAccountDescription ?? '';
|
||||
$standaloneAccountBackUrl = $ledgerStandaloneAccountBackUrl ?? '';
|
||||
$availableBreakdown = $ledgerAvailableBreakdown ?? [
|
||||
'bank_available' => (float) ($ledgerAccountSummary['balance'] ?? 0),
|
||||
'shop_available' => (float) ($ledgerShopSummary['balance'] ?? 0),
|
||||
@ -67,6 +71,7 @@
|
||||
$accountViewQuery = collect(request()->query())
|
||||
->except([
|
||||
'tab',
|
||||
'view',
|
||||
'accounts_page',
|
||||
'accounts_filter_column',
|
||||
'accounts_filter_value',
|
||||
@ -77,14 +82,23 @@
|
||||
])
|
||||
->filter(static fn ($value): bool => is_scalar($value) || $value === null)
|
||||
->all();
|
||||
$accountViewUrl = static fn (string $view): string => $ledgerPageUrl . '?' . http_build_query(array_merge(
|
||||
$accountViewQuery,
|
||||
['tab' => 'accounts', 'accounts_view' => $view],
|
||||
));
|
||||
$accountViewResetUrl = $ledgerPageUrl . '?' . http_build_query([
|
||||
'tab' => 'accounts',
|
||||
'accounts_view' => $accountView,
|
||||
]);
|
||||
$standaloneAccountViewUrl = static fn (string $view): string => route(
|
||||
'business.financial-ledger.account-basic',
|
||||
array_merge($accountViewQuery, ['view' => $view]),
|
||||
);
|
||||
$accountViewUrl = $standaloneAccountView === null
|
||||
? static fn (string $view): string => $ledgerPageUrl . '?' . http_build_query(array_merge(
|
||||
$accountViewQuery,
|
||||
['tab' => 'accounts', 'accounts_view' => $view],
|
||||
))
|
||||
: $standaloneAccountViewUrl;
|
||||
$accountViewResetUrl = $standaloneAccountView === null
|
||||
? $ledgerPageUrl . '?' . http_build_query([
|
||||
'tab' => 'accounts',
|
||||
'accounts_view' => $accountView,
|
||||
])
|
||||
: $standaloneAccountViewUrl($accountView);
|
||||
$accountFormUrl = $standaloneAccountView === null ? $tabUrl('accounts') : $accountViewResetUrl;
|
||||
$transactionView = $ledgerTransactionView ?? 'all';
|
||||
$transactionViews = $ledgerTransactionViews ?? [];
|
||||
$transactionViewAccount = $ledgerTransactionViewAccount ?? null;
|
||||
@ -688,42 +702,66 @@
|
||||
.ledger-flow-edit-actions [hidden] { display:none !important; }
|
||||
.ledger-flow-edit-actions .ledger-btn { color:var(--vip-warning, #935F07); }
|
||||
.ledger-mono { font-variant-numeric:tabular-nums; }
|
||||
.ledger-standalone-account-header { display:flex; align-items:flex-start; justify-content:space-between; gap:1rem; padding:1rem 1.15rem; margin-bottom:.9rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.75rem; background:#fff; }
|
||||
.ledger-standalone-account-header__copy { min-width:0; }
|
||||
.ledger-standalone-account-header__eyebrow { display:block; margin-bottom:.22rem; color:var(--vip-content-accent-dark, #856632); font-size:.66rem; font-weight:700; letter-spacing:.04em; }
|
||||
.ledger-standalone-account-header h2 { margin:0; color:var(--vip-content-ink, #171717); font-size:.95rem; font-weight:700; }
|
||||
.ledger-standalone-account-header p { margin:.3rem 0 0; color:var(--vip-content-muted, #595959); font-size:.72rem; line-height:1.55; }
|
||||
.ledger-standalone-account-back { display:inline-flex; flex:0 0 auto; align-items:center; gap:.35rem; color:var(--vip-content-accent-dark, #856632); font-size:.72rem; font-weight:600; text-decoration:none; }
|
||||
.ledger-standalone-account-back:hover { color:#6A5129; }
|
||||
@media (max-width: 900px) { .ledger-hero { align-items:flex-start; flex-direction:column; } .ledger-field { min-width:calc(50% - .5rem); } .ledger-edit-dialog .ledger-company-form__body { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
|
||||
@media (max-width: 700px) { .ledger-standalone-account-header { flex-direction:column; } }
|
||||
@media (max-width: 760px) { .ledger-company-form__body { grid-template-columns:repeat(2, minmax(0, 1fr)); } .ledger-company-field--wide { grid-column:span 2; } }
|
||||
@media (max-width: 520px) { .ledger-company-form__body { grid-template-columns:minmax(0, 1fr); } .ledger-company-field--wide { grid-column:auto; } }
|
||||
</style>
|
||||
|
||||
<div class="ledger-hero">
|
||||
<div>
|
||||
<h2>财务台账</h2>
|
||||
<p>账户、店铺、期初与动账明细统一归集;数据页默认本月 1 日至月底,可调整日期查看历史,流水口径:期末 = 期初 + 入账 − 出账。账户总表与店铺台账展示当前状态。</p>
|
||||
@if ($standaloneAccountView !== null)
|
||||
<header class="ledger-standalone-account-header" data-ledger-standalone-account="{{ $standaloneAccountView }}">
|
||||
<div class="ledger-standalone-account-header__copy">
|
||||
<span class="ledger-standalone-account-header__eyebrow">账户基本信息</span>
|
||||
<h2>{{ $standaloneAccountTitle }}</h2>
|
||||
<p>{{ $standaloneAccountDescription }}。当前为独立页面,筛选、列设置和账户编辑均只在本页进行。</p>
|
||||
</div>
|
||||
<a class="ledger-standalone-account-back" href="{{ $standaloneAccountBackUrl }}">
|
||||
<span class="material-icons-outlined" aria-hidden="true">arrow_back</span>
|
||||
返回账户基本信息
|
||||
</a>
|
||||
</header>
|
||||
@else
|
||||
<div class="ledger-hero">
|
||||
<div>
|
||||
<h2>财务台账</h2>
|
||||
<p>账户、店铺、期初与动账明细统一归集;数据页默认本月 1 日至月底,可调整日期查看历史,流水口径:期末 = 期初 + 入账 − 出账。账户总表与店铺台账展示当前状态。</p>
|
||||
</div>
|
||||
<div class="ledger-hero__meta">
|
||||
<span>账户 <b>{{ number_format((int) ($meta['accounts'] ?? 0)) }}</b></span>
|
||||
<span>店铺 <b>{{ number_format((int) ($meta['shops'] ?? 0)) }}</b></span>
|
||||
<span>公司 <b>{{ number_format((int) ($meta['companies'] ?? 0)) }}</b></span>
|
||||
<span>动账 <b data-ledger-meta-transactions>{{ number_format((int) ($meta['transactions'] ?? 0)) }}</b> 笔</span>
|
||||
<span>收入 <b>{{ $money($meta['income'] ?? 0) }}</b></span>
|
||||
<span>支出 <b>{{ $money($meta['expense'] ?? 0) }}</b></span>
|
||||
@if (!empty($meta['last_synced_at']))
|
||||
<span>数据时间 <b>{{ $date($meta['last_synced_at']) }}</b></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="ledger-hero__meta">
|
||||
<span>账户 <b>{{ number_format((int) ($meta['accounts'] ?? 0)) }}</b></span>
|
||||
<span>店铺 <b>{{ number_format((int) ($meta['shops'] ?? 0)) }}</b></span>
|
||||
<span>公司 <b>{{ number_format((int) ($meta['companies'] ?? 0)) }}</b></span>
|
||||
<span>动账 <b data-ledger-meta-transactions>{{ number_format((int) ($meta['transactions'] ?? 0)) }}</b> 笔</span>
|
||||
<span>收入 <b>{{ $money($meta['income'] ?? 0) }}</b></span>
|
||||
<span>支出 <b>{{ $money($meta['expense'] ?? 0) }}</b></span>
|
||||
@if (!empty($meta['last_synced_at']))
|
||||
<span>数据时间 <b>{{ $date($meta['last_synced_at']) }}</b></span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="ledger-tabbar" role="tablist" aria-label="财务台账页签">
|
||||
@foreach ($tabs as $key => $tab)
|
||||
<button type="button" class="ledger-tab {{ $activeTab === $key ? 'is-active' : '' }}" role="tab" aria-selected="{{ $activeTab === $key ? 'true' : 'false' }}" aria-controls="ledger-panel-{{ $key }}" data-ledger-tab="{{ $key }}">
|
||||
<span class="material-icons-outlined" aria-hidden="true">{{ $tab['icon'] }}</span>
|
||||
<span>{{ $tab['title'] }}</span>
|
||||
</button>
|
||||
@endforeach
|
||||
</nav>
|
||||
<nav class="ledger-tabbar" role="tablist" aria-label="财务台账页签">
|
||||
@foreach ($tabs as $key => $tab)
|
||||
<button type="button" class="ledger-tab {{ $activeTab === $key ? 'is-active' : '' }}" role="tab" aria-selected="{{ $activeTab === $key ? 'true' : 'false' }}" aria-controls="ledger-panel-{{ $key }}" data-ledger-tab="{{ $key }}">
|
||||
<span class="material-icons-outlined" aria-hidden="true">{{ $tab['icon'] }}</span>
|
||||
<span>{{ $tab['title'] }}</span>
|
||||
</button>
|
||||
@endforeach
|
||||
</nav>
|
||||
@endif
|
||||
|
||||
{{-- 账户总表 --}}
|
||||
<section class="ledger-panel" id="ledger-panel-accounts" role="tabpanel" data-ledger-panel="accounts" @if ($activeTab !== 'accounts') hidden @endif>
|
||||
<form class="ledger-toolbar" method="GET" action="{{ $tabUrl('accounts') }}">
|
||||
<input type="hidden" name="tab" value="accounts">
|
||||
<form class="ledger-toolbar" method="GET" action="{{ $accountFormUrl }}">
|
||||
@if ($standaloneAccountView === null)
|
||||
<input type="hidden" name="tab" value="accounts">
|
||||
@endif
|
||||
<input type="hidden" name="accounts_view" value="{{ $accountView }}">
|
||||
<div class="ledger-field"><label for="accounts-q">关键词</label><input id="accounts-q" name="accounts_q" value="{{ request('accounts_q') }}" placeholder="公司、账号、责任人"></div>
|
||||
<div class="ledger-field" style="min-width:11rem"><label for="accounts-short">账户简称</label><input id="accounts-short" name="accounts_short" list="ledger-account-list" value="{{ request('accounts_short') }}" placeholder="选择或输入"></div>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
</header>
|
||||
<div class="ledger-account-type-grid">
|
||||
@foreach ($ledgerAccountTypeCards as $card)
|
||||
<a class="ledger-account-type-card" href="{{ $accountViewUrl($card['view']) }}" data-account-type-card="{{ $card['view'] }}">
|
||||
<a class="ledger-account-type-card" href="{{ route('business.financial-ledger.account-basic', ['view' => $card['view']]) }}" data-account-type-card="{{ $card['view'] }}">
|
||||
<span class="ledger-account-type-card__icon material-icons-outlined" aria-hidden="true">{{ $card['icon'] }}</span>
|
||||
<div class="ledger-account-type-card__title">
|
||||
<strong>{{ $card['title'] }}</strong>
|
||||
|
||||
@ -1003,6 +1003,11 @@ Route::get('workspace/financial-operations/ledger/unretained-accounts', [\App\Ht
|
||||
->middleware('business.page:financial-operations,ledger')
|
||||
->name('business.financial-ledger.unretained-accounts');
|
||||
|
||||
Route::get('workspace/financial-operations/ledger/account-basic/{view}', [\App\Http\Controllers\Business\FinanceLedger::class, 'accountBasicPage'])
|
||||
->whereIn('view', ['bank', 'non_store', 'wangshang', 'wallet', 'other'])
|
||||
->middleware('business.page:financial-operations,ledger')
|
||||
->name('business.financial-ledger.account-basic');
|
||||
|
||||
Route::post('workspace/financial-ledger/accounts/todo', [\App\Http\Controllers\Business\FinanceLedger::class, 'updateAccountTodo'])
|
||||
->middleware('business.page:financial-operations,ledger')
|
||||
->name('business.financial-ledger.accounts.todo');
|
||||
|
||||
@ -8,6 +8,7 @@ use App\Models\Auth\User;
|
||||
use App\Utilities\BusinessNavigation;
|
||||
use App\Utilities\SidebarMenu;
|
||||
use App\Utilities\SidebarPermissions;
|
||||
use Modules\EcommerceFinance\View\FinanceLedgerPageData;
|
||||
use Tests\Feature\FeatureTestCase;
|
||||
|
||||
class FinanceLedgerNavigationTest extends FeatureTestCase
|
||||
@ -194,6 +195,37 @@ class FinanceLedgerNavigationTest extends FeatureTestCase
|
||||
$this->assertStringContainsString('accounts_view=bank', $content);
|
||||
$this->assertStringContainsString('accounts_view=wallet', $content);
|
||||
$this->assertStringContainsString('accounts_view=other', $content);
|
||||
|
||||
foreach (array_keys(FinanceLedgerPageData::ACCOUNT_TYPE_VIEWS) as $view) {
|
||||
$this->assertStringContainsString(
|
||||
'href="' . e(route('business.financial-ledger.account-basic', ['view' => $view])) . '"',
|
||||
$content,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_account_type_cards_open_standalone_account_pages(): void
|
||||
{
|
||||
$this->loginAs();
|
||||
|
||||
$backUrl = route('business.pages.show', [
|
||||
'section' => 'financial-operations',
|
||||
'page' => 'ledger',
|
||||
]) . '?tab=flows&flows_view=basic';
|
||||
|
||||
foreach (FinanceLedgerPageData::ACCOUNT_TYPE_VIEWS as $view => $definition) {
|
||||
$url = route('business.financial-ledger.account-basic', ['view' => $view]);
|
||||
$html = $this->get($url)
|
||||
->assertOk()
|
||||
->assertSeeText($definition['title'])
|
||||
->assertSeeText('返回账户基本信息')
|
||||
->getContent();
|
||||
|
||||
$this->assertStringContainsString('data-ledger-standalone-account="' . $view . '"', $html);
|
||||
$this->assertStringNotContainsString('data-ledger-tab="flows"', $html);
|
||||
$this->assertStringContainsString('action="' . e($url) . '"', $html);
|
||||
$this->assertStringContainsString('href="' . e($backUrl) . '"', $html);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_legacy_independent_company_reconciliation_permission_follows_the_renamed_page(): void
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user