seed(RolePermissionSeeder::class); } public function test_follow_up_assignment_is_visible_notified_and_only_creator_can_edit(): void { [$supervisor, $agent] = $this->team(); $lead = $this->lead($agent); $created = $this->actingAs($supervisor)->postJson('/api/follow-ups', [ 'lead_id' => $lead->id, 'user_id' => $agent->id, 'scheduled_at' => now()->addDay()->setTime(10, 0)->toISOString(), 'notes' => 'تماس درباره پیشنهاد', ])->assertCreated()->assertJsonPath('data.created_by', $supervisor->id); $followUpId = $created->json('data.id'); $this->assertDatabaseHas('internal_notifications', ['user_id' => $agent->id, 'type' => 'follow_up']); $this->actingAs($agent)->getJson('/api/follow-ups')->assertOk()->assertJsonFragment(['id' => $followUpId]); $this->actingAs($agent)->patchJson("/api/follow-ups/{$followUpId}", ['notes' => 'ویرایش غیرمجاز'])->assertForbidden(); $this->actingAs($supervisor)->patchJson("/api/follow-ups/{$followUpId}", ['notes' => 'ویرایش سازنده'])->assertOk(); $this->actingAs($agent)->patchJson("/api/follow-ups/{$followUpId}/mark-done")->assertOk()->assertJsonPath('data.status', 'completed'); } public function test_invoice_has_paid_balance_and_separate_approval_then_issue(): void { [$supervisor, $agent] = $this->team(); $lead = $this->lead($agent, ['final_result' => 'موفق', 'deal_value' => 1_000_000, 'sold_product' => 'اشتراک']); $invoiceId = $this->actingAs($agent)->postJson("/api/leads/{$lead->id}/invoice", [ 'paid_amount' => 250_000, ])->assertCreated()->assertJsonPath('payment_status', 'partial')->assertJsonPath('balance_due', 750000)->json('id'); $this->actingAs($supervisor)->postJson("/api/invoices/{$invoiceId}/reject", ['reason' => 'اصلاح مبلغ پرداختی']) ->assertOk()->assertJsonPath('status', 'rejected'); $this->actingAs($agent)->putJson("/api/invoices/{$invoiceId}", ['paid_amount' => 300_000]) ->assertOk()->assertJsonPath('status', 'pending_approval')->assertJsonPath('balance_due', 700000); $this->actingAs($supervisor)->postJson("/api/invoices/{$invoiceId}/approve") ->assertOk()->assertJsonPath('status', 'approved'); $this->actingAs($supervisor)->postJson("/api/invoices/{$invoiceId}/issue") ->assertOk()->assertJsonPath('status', 'issued'); } public function test_agent_can_refer_own_lead_to_team_agent_and_recipient_is_notified(): void { [$supervisor, $agent, $secondAgent] = $this->team(true); $lead = $this->lead($agent); $this->actingAs($agent)->postJson("/api/leads/{$lead->id}/refer", ['user_id' => $secondAgent->id]) ->assertOk()->assertJsonPath('assigned_to', $secondAgent->id); $this->assertDatabaseHas('internal_notifications', ['user_id' => $secondAgent->id, 'type' => 'assignment']); $managerLead = $this->lead($agent); $this->actingAs($agent)->postJson("/api/leads/{$managerLead->id}/refer", ['user_id' => $supervisor->id]) ->assertOk()->assertJsonPath('assigned_to', $supervisor->id); $this->assertDatabaseHas('internal_notifications', ['user_id' => $supervisor->id, 'type' => 'assignment']); } private function team(bool $withSecondAgent = false): array { $supervisor = User::factory()->create(['is_active' => true]); $supervisor->assignRole('supervisor'); $agent = User::factory()->create(['is_active' => true]); $agent->assignRole('agent'); $team = Team::create(['name' => fake()->unique()->company(), 'supervisor_id' => $supervisor->id, 'is_active' => true]); $supervisor->teams()->attach($team); $agent->teams()->attach($team); if (! $withSecondAgent) { return [$supervisor, $agent]; } $second = User::factory()->create(['is_active' => true]); $second->assignRole('agent'); $second->teams()->attach($team); return [$supervisor, $agent, $second]; } private function lead(User $agent, array $overrides = []): Lead { return Lead::create(array_merge([ 'company' => 'شرکت نمونه', 'first_name' => 'علی', 'last_name' => 'آزمایشی', 'phone' => fake()->unique()->numerify('0912#######'), 'assigned_to' => $agent->id, 'is_unassigned' => false, 'team_id' => $agent->teams()->value('teams.id'), ], $overrides)); } }