CaiWuXiTong/tests/Feature/Business/StoreReceivingBalanceOverviewEndpointTest.php
qiuxiang 59485b99f2 fix: period balance reconciliation performance
Separate trend loading, aggregate summary from store balances, and cap trend queries to five representative dates.
2026-11-11 19:57:36 +08:00

341 lines
13 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use Mockery;
use Modules\EcommerceFinance\Services\PeriodBalanceReconciliationService;
use Modules\EcommerceFinance\Services\StoreReceivingBalanceOverviewService;
use Tests\Feature\FeatureTestCase;
class StoreReceivingBalanceOverviewEndpointTest extends FeatureTestCase
{
public function test_overview_endpoint_returns_the_selected_month_payload(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-01', 'end' => '2026-10-31'],
'summary' => [
'opening_balance' => 100.0,
'income' => 30.0,
'expense' => 10.0,
'balance' => 120.0,
'platform_count' => 8,
'available_platform_count' => 8,
'complete' => true,
],
'platforms' => [],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('overview')->once()->with('2026-10')->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview', ['month' => '2026-10']))
->assertOk()
->assertJsonPath('month', '2026-10')
->assertJsonPath('summary.balance', 120)
->assertJsonPath('summary.complete', true);
}
public function test_overview_endpoint_accepts_an_explicit_date_range(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-05', 'end' => '2026-10-20'],
'summary' => [
'opening_balance' => 100.0,
'income' => 30.0,
'expense' => 10.0,
'balance' => 120.0,
'platform_count' => 8,
'available_platform_count' => 8,
'complete' => true,
],
'platforms' => [],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('overview')
->once()
->with('2026-10-05', '2026-10-20')
->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview', [
'start' => '2026-10-05',
'end' => '2026-10-20',
]))
->assertOk()
->assertJsonPath('period.start', '2026-10-05')
->assertJsonPath('period.end', '2026-10-20')
->assertJsonPath('summary.complete', true);
}
public function test_overview_page_uses_the_vue_mount_point(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'billing-transactions',
'page' => 'store-receiving-bills',
'tab' => 'balance-overview',
]))
->assertOk()
->assertSee('class="biz-card mt-6 business-blank-page"', false)
->assertSee('public/js/business/pages.min.js', false);
}
public function test_reconciliation_page_uses_the_same_vue_entry_point(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'billing-transactions',
'page' => 'period-balance-reconciliation',
]))
->assertOk()
->assertSee('class="biz-card mt-6 business-blank-page"', false)
->assertSee('public/js/business/pages.min.js', false);
}
public function test_reconciliation_endpoint_accepts_an_explicit_date_range(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-05', 'end' => '2026-10-20'],
'tolerance_yuan' => 200,
'financial' => ['summary' => ['balance' => 120.0], 'platforms' => []],
'dual_source' => ['status' => 'incomplete'],
'sources' => [],
'platforms' => [],
'stores' => [],
'summary' => ['connected_store_count' => 0],
'trend' => ['points' => [], 'by_platform' => [], 'by_store' => []],
];
$service = Mockery::mock(PeriodBalanceReconciliationService::class);
$service->shouldReceive('build')
->once()
->with('2026-10-05', '2026-10-20')
->andReturn($payload);
$this->app->instance(PeriodBalanceReconciliationService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.reconciliation', [
'start' => '2026-10-05',
'end' => '2026-10-20',
]))
->assertOk()
->assertJsonPath('period.start', '2026-10-05')
->assertJsonPath('period.end', '2026-10-20')
->assertJsonPath('tolerance_yuan', 200);
}
public function test_reconciliation_trend_endpoint_loads_separately(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'points' => [[
'date' => '2026-10-31',
'financial_balance' => 120.0,
'jushuitan_balance' => 110.0,
'difference' => 10.0,
'status' => 'normal',
]],
'by_platform' => [],
'by_store' => [],
'dates_note' => '趋势仅按现有聚水潭快照日期绘制,不推算缺失日期。',
];
$service = Mockery::mock(PeriodBalanceReconciliationService::class);
$service->shouldReceive('buildTrend')
->once()
->with('2026-10-01', '2026-10-31')
->andReturn($payload);
$this->app->instance(PeriodBalanceReconciliationService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.reconciliation.trend', [
'start' => '2026-10-01',
'end' => '2026-10-31',
]))
->assertOk()
->assertJsonPath('points.0.financial_balance', 120)
->assertJsonPath('points.0.status', 'normal');
}
public function test_income_detail_endpoint_returns_platform_store_and_bill_item_rows(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-01', 'end' => '2026-10-31'],
'summary' => [
'income' => 120.0,
'platform_count' => 8,
'available_platform_count' => 8,
'complete' => true,
],
'details' => [[
'platform_slug' => 'taobao-alipay',
'platform' => '淘系支付宝账户',
'store_name' => '测试店铺',
'bill_item' => '货款收入',
'amount' => 120.0,
]],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('incomeDetail')->once()->with('2026-10')->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.income-detail', [
'month' => '2026-10',
]))
->assertOk()
->assertJsonPath('summary.income', 120)
->assertJsonPath('details.0.platform_slug', 'taobao-alipay')
->assertJsonPath('details.0.bill_item', '货款收入');
}
public function test_expense_detail_endpoint_returns_platform_store_and_bill_item_rows(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-01', 'end' => '2026-10-31'],
'summary' => [
'expense' => 88.0,
'platform_count' => 8,
'available_platform_count' => 8,
'complete' => true,
],
'details' => [[
'platform_slug' => 'jd-wallet',
'platform' => '京东网银钱包账户',
'store_name' => '测试店铺',
'bill_item' => '平台扣费',
'amount' => 88.0,
]],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('expenseDetail')->once()->with('2026-10')->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.expense-detail', [
'month' => '2026-10',
]))
->assertOk()
->assertJsonPath('summary.expense', 88)
->assertJsonPath('details.0.platform_slug', 'jd-wallet')
->assertJsonPath('details.0.bill_item', '平台扣费');
}
public function test_balance_detail_endpoint_returns_platform_closing_balances(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-01', 'end' => '2026-10-31'],
'summary' => [
'balance' => 1049.07,
'platform_count' => 8,
'available_platform_count' => 8,
'complete' => true,
],
'details' => [[
'platform_slug' => 'taobao-alipay',
'platform' => '淘系支付宝账户',
'store_name' => '',
'bill_item' => '',
'amount' => 218.88,
]],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('balanceDetail')->once()->with('2026-10')->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.balance-detail', [
'month' => '2026-10',
]))
->assertOk()
->assertJsonPath('summary.balance', 1049.07)
->assertJsonPath('details.0.platform_slug', 'taobao-alipay')
->assertJsonPath('details.0.amount', 218.88);
}
public function test_balance_store_detail_endpoint_returns_store_closing_balances(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
$payload = [
'month' => '2026-10',
'period' => ['start' => '2026-10-01', 'end' => '2026-10-31'],
'summary' => [
'balance' => 1049.07,
'platform_count' => 8,
'available_platform_count' => 8,
'store_count' => 1,
'ready_store_count' => 1,
'complete' => true,
],
'details' => [[
'platform_slug' => 'taobao-alipay',
'platform' => '淘系支付宝账户',
'store_name' => '测试店铺',
'bill_item' => '',
'opening_balance' => 100.0,
'income' => 130.0,
'expense' => 10.0,
'amount' => 220.0,
'available' => true,
]],
];
$service = Mockery::mock(StoreReceivingBalanceOverviewService::class);
$service->shouldReceive('balanceStoreDetail')->once()->with('2026-10')->andReturn($payload);
$this->app->instance(StoreReceivingBalanceOverviewService::class, $service);
$this->loginAs()
->getJson(route('ecommerce-finance.account-bills.overview.balance-store-detail', [
'month' => '2026-10',
]))
->assertOk()
->assertJsonPath('summary.complete', true)
->assertJsonPath('details.0.store_name', '测试店铺')
->assertJsonPath('details.0.amount', 220);
}
}