326 lines
13 KiB
PHP
326 lines
13 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Modules\EcommerceFinance\View\ShopFlowQuery;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class ShopFlowPageTest extends FeatureTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config(['upstream_mirrors.connection' => 'sqlite']);
|
|
app('url')->defaults(['company_id' => $this->company->id]);
|
|
$this->createFinanceTables();
|
|
$this->seedFinanceData();
|
|
}
|
|
|
|
public function test_selected_shop_locks_account_and_builds_balanced_flow_summary(): void
|
|
{
|
|
$data = app(ShopFlowQuery::class)->pageData(new Request([
|
|
'company' => '测试主体',
|
|
'shop' => '测试店铺A',
|
|
'account' => '其他账户',
|
|
'start' => '2026-11-01',
|
|
'end' => '2026-11-02',
|
|
'direction' => 'all',
|
|
'source_type' => 'all',
|
|
]));
|
|
|
|
$this->assertSame('测试账户', $data['shopFlowSelectedAccountShort']);
|
|
$this->assertSame('测试店铺A', $data['shopFlowSelectedShop']['shop_name']);
|
|
$this->assertCount(2, $data['shopFlowRelatedShops']);
|
|
$this->assertSame(4, $data['shopFlowSummary']['income_count']);
|
|
$this->assertSame(60.0, $data['shopFlowSummary']['income']);
|
|
$this->assertSame(3, $data['shopFlowSummary']['withdraw_count']);
|
|
$this->assertSame(1, $data['shopFlowSummary']['pending_count']);
|
|
$this->assertSame(1, $data['shopFlowSummary']['expense_count']);
|
|
$this->assertSame(25.0, $data['shopFlowSummary']['expense']);
|
|
$this->assertSame('matched', $data['shopFlowBalance']['status']);
|
|
$this->assertSame(135.0, $data['shopFlowBalance']['closing']);
|
|
$this->assertCount(5, $data['shopFlowRows']->items());
|
|
$this->assertSame(135.0, $data['shopFlowRows']->items()[0]['balance']);
|
|
$this->assertStringContainsString(
|
|
'account=' . rawurlencode('测试账户'),
|
|
$data['shopFlowUnlockUrl'],
|
|
);
|
|
|
|
$shopRows = collect($data['shopFlowRows']->items())
|
|
->where('source', 'shop')
|
|
->values();
|
|
$matched = $shopRows->firstWhere('match_status', '已匹配提现单');
|
|
$pending = $shopRows->firstWhere('match_status', '待匹配提现单');
|
|
$this->assertSame('测试店铺A', $matched['shop_name']);
|
|
$this->assertSame('', $pending['shop_name']);
|
|
}
|
|
|
|
public function test_detail_filters_do_not_change_period_summary_or_balance_check(): void
|
|
{
|
|
$data = app(ShopFlowQuery::class)->pageData(new Request([
|
|
'account' => '测试账户',
|
|
'start' => '2026-11-01',
|
|
'end' => '2026-11-02',
|
|
'direction' => 'expense',
|
|
'source_type' => 'dingtalk',
|
|
]));
|
|
|
|
$this->assertNull($data['shopFlowSelectedShop']);
|
|
$this->assertCount(2, $data['shopFlowRelatedShops']);
|
|
$this->assertSame(60.0, $data['shopFlowSummary']['income']);
|
|
$this->assertSame(25.0, $data['shopFlowSummary']['expense']);
|
|
$this->assertCount(1, $data['shopFlowRows']->items());
|
|
$this->assertSame('expense_dingtalk', $data['shopFlowRows']->items()[0]['type_key']);
|
|
$this->assertSame('matched', $data['shopFlowBalance']['status']);
|
|
}
|
|
|
|
public function test_page_renders_with_export_and_ledger_table_classes(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'financial-operations',
|
|
'page' => 'shop-flows',
|
|
]) . '?shop=' . rawurlencode('测试店铺A')
|
|
. '&start=2026-11-01&end=2026-11-02')
|
|
->assertOk()
|
|
->assertSeeText('店铺收支明细')
|
|
->assertSeeText('测试店铺A')
|
|
->assertSeeText('¥60.00')
|
|
->assertSeeText('查看整张卡')
|
|
->assertSee('data-shop-flow-page', false)
|
|
->assertSee('data-shop-flow-export', false)
|
|
->assertSee('ledger-table', false)
|
|
->assertSee('/workspace/financial-operations/shop-flows/shop-flows/export', false);
|
|
}
|
|
|
|
public function test_csv_export_streams_filtered_rows_with_utf8_bom(): void
|
|
{
|
|
$url = route('business.pages.export', [
|
|
'section' => 'financial-operations',
|
|
'page' => 'shop-flows',
|
|
'tab' => 'shop-flows',
|
|
]) . '?' . http_build_query([
|
|
'shop' => '测试店铺A',
|
|
'start' => '2026-11-01',
|
|
'end' => '2026-11-02',
|
|
'direction' => 'income',
|
|
'source_type' => 'shop',
|
|
]);
|
|
|
|
$request = Request::create($url);
|
|
$this->assertSame('测试店铺A', $request->query('shop'));
|
|
$this->assertSame(
|
|
'测试账户',
|
|
app(ShopFlowQuery::class)->pageData($request)['shopFlowSelectedAccountShort'],
|
|
);
|
|
|
|
$directResponse = app(ShopFlowQuery::class)->export($request);
|
|
ob_start();
|
|
$directResponse->sendContent();
|
|
$directContent = ob_get_clean();
|
|
$this->assertStringContainsString('测试店铺A', $directContent);
|
|
|
|
$response = $this->loginAs()->get($url)->assertOk();
|
|
$response->assertHeader('content-type', 'text/csv; charset=UTF-8');
|
|
$content = $response->streamedContent();
|
|
|
|
$this->assertStringStartsWith("\xEF\xBB\xBF", $content);
|
|
$this->assertStringContainsString('测试店铺A', $content);
|
|
$this->assertStringContainsString('已匹配提现单', $content);
|
|
$this->assertStringContainsString('待匹配提现单', $content);
|
|
$this->assertStringNotContainsString('供应商退款', $content);
|
|
$this->assertStringNotContainsString('付款审批', $content);
|
|
}
|
|
|
|
private function createFinanceTables(): void
|
|
{
|
|
Schema::connection('sqlite')->create('fin_companies', function ($table): void {
|
|
$table->increments('id');
|
|
$table->string('name');
|
|
$table->string('tax_type')->nullable();
|
|
$table->string('basic_bank')->nullable();
|
|
});
|
|
Schema::connection('sqlite')->create('fin_accounts', function ($table): void {
|
|
$table->increments('id');
|
|
$table->string('company_name');
|
|
$table->string('account_short');
|
|
$table->string('account_no')->nullable();
|
|
$table->string('bank_name')->nullable();
|
|
$table->string('account_last4')->nullable();
|
|
$table->decimal('current_balance', 18, 2)->default(0);
|
|
$table->string('owner')->nullable();
|
|
});
|
|
Schema::connection('sqlite')->create('fin_shops', function ($table): void {
|
|
$table->increments('id');
|
|
$table->string('shop_name');
|
|
$table->string('platform')->nullable();
|
|
$table->string('company_name')->nullable();
|
|
$table->string('account_short')->nullable();
|
|
$table->string('status')->nullable();
|
|
$table->string('payout_account')->nullable();
|
|
});
|
|
Schema::connection('sqlite')->create('fin_transactions', function ($table): void {
|
|
$table->increments('id');
|
|
$table->date('txn_date');
|
|
$table->string('account_short')->nullable();
|
|
$table->text('detail')->nullable();
|
|
$table->decimal('income', 18, 2)->default(0);
|
|
$table->decimal('expense', 18, 2)->default(0);
|
|
$table->string('approval_no')->nullable();
|
|
$table->string('source');
|
|
$table->string('source_value')->nullable();
|
|
$table->string('process_type')->nullable();
|
|
$table->string('row_key');
|
|
});
|
|
Schema::connection('sqlite')->create('fin_daily_flows', function ($table): void {
|
|
$table->increments('id');
|
|
$table->date('flow_date');
|
|
$table->string('account_short');
|
|
$table->decimal('closing_balance', 18, 2)->default(0);
|
|
});
|
|
Schema::connection('sqlite')->create('fin_account_opening', function ($table): void {
|
|
$table->increments('id');
|
|
$table->string('account_short');
|
|
$table->date('opening_date');
|
|
$table->decimal('opening_balance', 18, 2)->default(0);
|
|
});
|
|
Schema::connection('sqlite')->create('fin_account_map', function ($table): void {
|
|
$table->increments('id');
|
|
$table->string('source');
|
|
$table->string('source_value');
|
|
$table->string('account_short')->nullable();
|
|
});
|
|
}
|
|
|
|
private function seedFinanceData(): void
|
|
{
|
|
DB::table('fin_companies')->insert([
|
|
'name' => '测试主体',
|
|
'tax_type' => '一般纳税人',
|
|
'basic_bank' => '测试银行',
|
|
]);
|
|
DB::table('fin_accounts')->insert([
|
|
'company_name' => '测试主体',
|
|
'account_short' => '测试账户',
|
|
'account_no' => '6222000012345678',
|
|
'bank_name' => '测试银行',
|
|
'account_last4' => '5678',
|
|
'current_balance' => 120,
|
|
'owner' => '测试户名',
|
|
]);
|
|
DB::table('fin_shops')->insert([
|
|
[
|
|
'shop_name' => '测试店铺A',
|
|
'platform' => '京东',
|
|
'company_name' => '测试主体',
|
|
'account_short' => '测试账户',
|
|
'status' => '在营',
|
|
'payout_account' => '测试收款账号',
|
|
],
|
|
[
|
|
'shop_name' => '测试店铺B',
|
|
'platform' => '京东',
|
|
'company_name' => '测试主体',
|
|
'account_short' => '测试账户',
|
|
'status' => '在营',
|
|
'payout_account' => '测试收款账号',
|
|
],
|
|
]);
|
|
DB::table('fin_daily_flows')->insert([
|
|
[
|
|
'flow_date' => '2026-10-31',
|
|
'account_short' => '测试账户',
|
|
'closing_balance' => 100,
|
|
],
|
|
[
|
|
'flow_date' => '2026-11-02',
|
|
'account_short' => '测试账户',
|
|
'closing_balance' => 135,
|
|
],
|
|
]);
|
|
DB::table('fin_account_opening')->insert([
|
|
'account_short' => '测试账户',
|
|
'opening_date' => '2026-10-01',
|
|
'opening_balance' => 80,
|
|
]);
|
|
DB::table('fin_transactions')->insert([
|
|
[
|
|
'txn_date' => '2026-11-01',
|
|
'account_short' => '测试账户',
|
|
'detail' => '京东账单提现 · 测试店铺A',
|
|
'income' => 30,
|
|
'expense' => 0,
|
|
'approval_no' => 'JD:W001',
|
|
'source' => 'shop',
|
|
'source_value' => '测试店铺A',
|
|
'process_type' => '店铺提现',
|
|
'row_key' => 'shop-match',
|
|
],
|
|
[
|
|
'txn_date' => '2026-11-01',
|
|
'account_short' => '测试账户',
|
|
'detail' => '供应商退款',
|
|
'income' => 10,
|
|
'expense' => 0,
|
|
'approval_no' => 'PAY-001',
|
|
'source' => 'payable',
|
|
'source_value' => '供应商甲',
|
|
'process_type' => '供应商退款',
|
|
'row_key' => 'other-income',
|
|
],
|
|
[
|
|
'txn_date' => '2026-11-01',
|
|
'account_short' => '测试账户',
|
|
'detail' => '店铺提现待匹配',
|
|
'income' => 5,
|
|
'expense' => 0,
|
|
'approval_no' => 'SHOP-UNMATCHED',
|
|
'source' => 'shop',
|
|
'source_value' => null,
|
|
'process_type' => '店铺提现',
|
|
'row_key' => 'shop-pending',
|
|
],
|
|
[
|
|
'txn_date' => '2026-11-02',
|
|
'account_short' => '',
|
|
'detail' => '付款审批',
|
|
'income' => 0,
|
|
'expense' => 25,
|
|
'approval_no' => 'DING-001',
|
|
'source' => 'dingtalk',
|
|
'source_value' => '测试账户',
|
|
'process_type' => '付款审批',
|
|
'row_key' => 'ding-expense',
|
|
],
|
|
[
|
|
'txn_date' => '2026-11-01',
|
|
'account_short' => '测试账户',
|
|
'detail' => '店铺账单提现 · 测试店铺B',
|
|
'income' => 15,
|
|
'expense' => 0,
|
|
'approval_no' => 'BILL-001',
|
|
'source' => 'shop_bill',
|
|
'source_value' => '测试店铺B',
|
|
'process_type' => '店铺提现',
|
|
'row_key' => 'shop-bill-match',
|
|
],
|
|
]);
|
|
DB::table('fin_account_map')->insert([
|
|
[
|
|
'source' => 'payable',
|
|
'source_value' => '供应商甲',
|
|
'account_short' => '测试账户',
|
|
],
|
|
[
|
|
'source' => 'dingtalk',
|
|
'source_value' => '测试账户',
|
|
'account_short' => '测试账户',
|
|
],
|
|
]);
|
|
}
|
|
}
|