CaiWuXiTong/tests/Feature/Business/FinanceLedgerManualEditTest.php
92451 040d0d08ae feat: 接入公司支付宝账单与台账手动编辑 (#797)
接入公司非店铺支付宝账单、受限批处理导入,并为账户总表及每日资金流水增加管理员手动编辑与审计。
Co-authored-by: 92451 <924518135@qq.com>
Co-committed-by: 92451 <924518135@qq.com>
2026-10-30 20:03:04 +08:00

278 lines
11 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Models\Auth\Role;
use Illuminate\Support\Facades\DB;
use Modules\EcommerceFinance\Services\FinanceLedgerCalculator;
use Tests\Feature\FeatureTestCase;
class FinanceLedgerManualEditTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail(),
);
$connection = DB::connection('business_mirror');
$accountId = $connection->table('fin_accounts')->insertGetId([
'company_name' => '手动编辑测试公司',
'account_short' => '手动编辑测试账户',
'account_type' => '公户',
'account_no' => '6222000000008899',
'current_balance' => 100,
'daily_available' => 100,
]);
$this->accountId = (int) $accountId;
$connection->table('fin_account_opening')->insert([
'account_short' => '手动编辑测试账户',
'opening_date' => '2026-10-17',
'opening_balance' => 100,
]);
$connection->table('fin_transactions')->insert([
[
'txn_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
'detail' => '自动入账',
'income' => 25,
'expense' => 0,
'source' => 'manual',
'process_type' => '手工录入',
'row_key' => hash('sha256', 'manual-edit-income'),
],
[
'txn_date' => '2026-10-19',
'account_short' => '手动编辑测试账户',
'detail' => '自动出账',
'income' => 0,
'expense' => 30,
'source' => 'manual',
'process_type' => '手工录入',
'row_key' => hash('sha256', 'manual-edit-expense'),
],
]);
app(FinanceLedgerCalculator::class)->recalculate();
}
private int $accountId;
public function test_account_and_daily_flow_edit_controls_are_visible(): void
{
$html = $this->loginAs()->get($this->pageUrl())->assertOk()->getContent();
$this->assertStringContainsString('data-ledger-account-edit', $html);
$this->assertStringContainsString('data-ledger-account-dialog', $html);
$this->assertStringContainsString('data-ledger-flow-edit', $html);
$this->assertStringContainsString('data-ledger-flow-dialog', $html);
}
public function test_account_editor_updates_fields_renames_references_and_writes_audit(): void
{
$this->loginAs()->get($this->accountEditUrl())
->assertOk()
->assertJsonPath('account.id', $this->accountId)
->assertJsonPath('account.account_short', '手动编辑测试账户');
$this->loginAs()->postJson($this->accountUpdateUrl(), [
'company_name' => '手动编辑测试公司',
'account_short' => '手动编辑测试账户新',
'account_type' => '公户',
'status' => 'limited',
'region' => '苏州',
'account_no' => '6222000000008899',
'bank_name' => '测试银行',
'bank_city_branch' => '苏州分行',
'company_plan' => '待注销',
'account_should_keep' => '否',
'single_limit' => '100000',
'daily_limit' => '200000',
'monthly_limit' => '300000',
'yearly_limit' => '400000',
'clearing_days' => 2.5,
'raise_target' => '500000',
'raise_status' => '处理中',
'raise_actions' => '提升限额',
'done_actions' => '提交资料',
'next_action' => '等待审核',
'remark' => '账户手动修改',
'owner' => '财务负责人',
])->assertOk()->assertJsonPath('account.account_short', '手动编辑测试账户新');
$connection = DB::connection('business_mirror');
$this->assertDatabaseHas('fin_accounts', [
'id' => $this->accountId,
'account_short' => '手动编辑测试账户新',
'region' => '苏州',
'bank_name' => '测试银行',
'owner' => '财务负责人',
], 'business_mirror');
foreach (['fin_account_opening', 'fin_daily_flows', 'fin_transactions', 'fin_shops'] as $table) {
$this->assertSame(
0,
$connection->table($table)->where('account_short', '手动编辑测试账户')->count(),
$table . ' 仍保留旧账户简称',
);
}
$this->assertDatabaseHas('fin_account_statuses', [
'account_short' => '手动编辑测试账户新',
'status' => 'limited',
], 'business_mirror');
$this->assertDatabaseHas('fin_ledger_edit_events', [
'entity_type' => 'account',
'entity_key' => (string) $this->accountId,
'user_name' => 'Test User',
], 'business_mirror');
}
public function test_daily_flow_override_cascades_and_can_reset_to_automatic(): void
{
$this->loginAs()->get($this->flowEditUrl('2026-10-18'))
->assertOk()
->assertJsonPath('flow.opening_balance', 100)
->assertJsonPath('flow.inflow', 25)
->assertJsonPath('flow.closing_balance', 125);
$this->loginAs()->postJson($this->flowUpdateUrl(), [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
'opening_balance' => 100,
'inflow' => 50,
'outflow' => 10,
'closing_balance' => 140,
'remark' => '银行流水手工校正',
])->assertOk()
->assertJsonPath('flow.manual_override', true)
->assertJsonPath('flow.closing_balance', 140);
$connection = DB::connection('business_mirror');
$this->assertDatabaseHas('fin_daily_flows', [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
'inflow' => 50,
'outflow' => 10,
'closing_balance' => 140,
'manual_override' => 1,
'updated_by' => 'Test User',
], 'business_mirror');
$this->assertDatabaseHas('fin_daily_flows', [
'flow_date' => '2026-10-19',
'account_short' => '手动编辑测试账户',
'opening_balance' => 140,
'outflow' => 30,
'closing_balance' => 110,
], 'business_mirror');
$this->assertSame(110.0, (float) $connection->table('fin_accounts')
->where('id', $this->accountId)
->value('current_balance'));
$this->loginAs()->postJson($this->flowResetUrl(), [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
])->assertOk()->assertJsonPath('flow.manual_override', false);
$this->assertDatabaseMissing('fin_daily_flow_overrides', [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
], 'business_mirror');
$this->assertDatabaseHas('fin_daily_flows', [
'flow_date' => '2026-10-19',
'account_short' => '手动编辑测试账户',
'opening_balance' => 125,
'closing_balance' => 95,
], 'business_mirror');
}
public function test_manual_edit_validates_equation_duplicate_short_and_permission(): void
{
$this->withExceptionHandling()->loginAs()->postJson($this->flowUpdateUrl(), [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
'opening_balance' => 100,
'inflow' => 10,
'outflow' => 5,
'closing_balance' => 120,
])->assertUnprocessable()->assertJsonValidationErrors('closing_balance');
$connection = DB::connection('business_mirror');
$connection->table('fin_accounts')->insert([
'company_name' => '重名公司',
'account_short' => '已存在账户简称',
]);
$this->withExceptionHandling()->loginAs()->postJson($this->accountUpdateUrl(), [
'company_name' => '手动编辑测试公司',
'account_short' => '已存在账户简称',
'status' => 'normal',
])->assertUnprocessable()->assertJsonValidationErrors('account_short');
$viewer = user_model_class()::create([
'name' => '台账只读用户',
'email' => 'ledger-viewer-' . uniqid() . '@example.com',
'password' => 'local-test-password',
'locale' => 'zh-CN',
'enabled' => 1,
'landing_page' => 'dashboard',
'created_from' => 'core::test',
]);
$viewer->companies()->attach($this->company->id);
$viewer->roles()->attach(Role::where('name', 'user')->firstOrFail()->id);
$viewer->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->withExceptionHandling()->loginAs($viewer)->postJson($this->flowUpdateUrl(), [
'flow_date' => '2026-10-18',
'account_short' => '手动编辑测试账户',
'opening_balance' => 100,
'inflow' => 10,
'outflow' => 5,
'closing_balance' => 105,
])->assertForbidden();
}
private function pageUrl(): string
{
return route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'financial-operations',
'page' => 'ledger',
]) . '?tab=flows';
}
private function accountEditUrl(): string
{
return route('business.financial-ledger.accounts.edit', [
'company_id' => $this->company->id,
'id' => $this->accountId,
]);
}
private function accountUpdateUrl(): string
{
return route('business.financial-ledger.accounts.update', [
'company_id' => $this->company->id,
'id' => $this->accountId,
]);
}
private function flowEditUrl(string $date): string
{
return route('business.financial-ledger.flows.edit', [
'company_id' => $this->company->id,
'flow_date' => $date,
'account_short' => '手动编辑测试账户',
]);
}
private function flowUpdateUrl(): string
{
return route('business.financial-ledger.flows.update', ['company_id' => $this->company->id]);
}
private function flowResetUrl(): string
{
return route('business.financial-ledger.flows.reset', ['company_id' => $this->company->id]);
}
}