diff --git a/app/Http/Controllers/Admin/SalesController.php b/app/Http/Controllers/Admin/SalesController.php index 6fdc97c..a85d1b2 100644 --- a/app/Http/Controllers/Admin/SalesController.php +++ b/app/Http/Controllers/Admin/SalesController.php @@ -85,6 +85,16 @@ class SalesController extends Controller $invoicesQuery->whereBetween('created_at', [$start, $end]); $dispenseQuery->whereBetween('machine_time', [$start, $end]); + // 機台權限隔離:非系統管理員只能看到「自己可存取機台」的交易。沿用 Machine 的 machine_access 全域 scope + // 取得可存取機台集合(與上方機台下拉清單一致),修正訂單/發票/出貨清單先前未依機台授權過濾、 + // 導致無該機台權限的帳號仍可見其交易紀錄的資料隔離漏洞。 + if (!auth()->user()->isSystemAdmin()) { + $accessibleMachineIds = Machine::pluck('id'); + $ordersQuery->whereIn('machine_id', $accessibleMachineIds); + $invoicesQuery->whereIn('machine_id', $accessibleMachineIds); + $dispenseQuery->whereIn('machine_id', $accessibleMachineIds); + } + // 共用過濾器:機台 if ($machineId) { $ordersQuery->where('machine_id', $machineId); diff --git a/tests/Feature/Admin/SalesMachineAccessTest.php b/tests/Feature/Admin/SalesMachineAccessTest.php new file mode 100644 index 0000000..8a3a120 --- /dev/null +++ b/tests/Feature/Admin/SalesMachineAccessTest.php @@ -0,0 +1,100 @@ + 'menu.sales', 'guard_name' => 'web']); + $this->company = Company::create(['name' => 'Co', 'code' => 'CO']); + $role = Role::create(['name' => 'r', 'company_id' => $this->company->id, 'is_system' => false, 'guard_name' => 'web']); + $role->syncPermissions(['menu.sales']); + + $this->main = User::factory()->create(['company_id' => $this->company->id, 'is_admin' => true, 'level' => 0]); + $this->main->assignRole($role); + + $this->mA = Machine::factory()->create(['company_id' => $this->company->id]); + $this->mB = Machine::factory()->create(['company_id' => $this->company->id]); + + $this->orderA = $this->makeOrder($this->mA->id); + $this->orderB = $this->makeOrder($this->mB->id); + } + + private function makeOrder(int $machineId): int + { + return DB::table('orders')->insertGetId([ + 'company_id' => $this->company->id, + 'machine_id' => $machineId, + 'payment_type' => 1, + 'total_amount' => 100, + 'pay_amount' => 100, + 'payment_status' => 1, + 'status' => 'completed', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + private function visibleOrderIds(User $user): array + { + $response = $this->actingAs($user)->get(route('admin.sales.index')); + $response->assertOk(); + return collect($response->viewData('orders')->items())->pluck('id')->all(); + } + + public function test_sub_account_with_machine_access_sees_only_that_machine_orders(): void + { + $sub = User::factory()->create(['company_id' => $this->company->id, 'is_admin' => false, 'level' => 1, 'parent_id' => $this->main->id]); + $sub->assignRole(Role::where('company_id', $this->company->id)->first()); + $sub->machines()->attach($this->mA->id); // 只被授權 mA + + $ids = $this->visibleOrderIds($sub); + $this->assertContains($this->orderA, $ids); + $this->assertNotContains($this->orderB, $ids); // 未授權 mB → 看不到其訂單 + } + + public function test_sub_account_without_any_machine_access_sees_no_orders(): void + { + $sub = User::factory()->create(['company_id' => $this->company->id, 'is_admin' => false, 'level' => 1, 'parent_id' => $this->main->id]); + $sub->assignRole(Role::where('company_id', $this->company->id)->first()); + // 不授權任何機台 + + $ids = $this->visibleOrderIds($sub); + $this->assertNotContains($this->orderA, $ids); + $this->assertNotContains($this->orderB, $ids); + } + + public function test_system_admin_sees_all_orders(): void + { + $sysAdmin = User::factory()->create(['company_id' => null]); + + $ids = $this->visibleOrderIds($sysAdmin); + $this->assertContains($this->orderA, $ids); + $this->assertContains($this->orderB, $ids); + } +}