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('/]*data-ledger-header-details/', $html)); $this->assertSame(71, preg_match_all('/]*data-ledger-header-label/', $html)); $this->assertSame(2, preg_match_all('/]*data-ledger-date-input>]*data-ledger-date-picker>/', $html, ), $inputId . ' 应提供可点击的日历按钮', ); } } 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, '', $panelStart); $theadEnd = strpos($html, '', $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>平台', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>ERP系统店铺名称', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>店铺主体', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>店铺负责人', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>部门负责人', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>余额来源', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>预留标准', $headerHtml); $this->assertStringContainsString('data-ledger-header-label>余额预警', $headerHtml); $this->assertStringNotContainsString('data-ledger-header-label>店铺名称', $headerHtml); $this->assertStringNotContainsString('data-ledger-header-label>所属公司', $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('