Deploy Ladill Care / deploy (push) Successful in 29s
Free keeps core records, appointments, and basic workflows; Pro unlocks lab, pharmacy, billing, and Queue. Both paid tiers bill per branch with unlimited branches. Enterprise adds multi-dept workflows, analytics, AI-assisted healthcare integration, and priority support (not Afia).
162 lines
5.2 KiB
PHP
162 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Http\Middleware\EnsurePlatformSession;
|
|
use App\Models\Bill;
|
|
use App\Models\Branch;
|
|
use App\Models\Consultation;
|
|
use App\Models\Member;
|
|
use App\Models\Organization;
|
|
use App\Models\Patient;
|
|
use App\Models\User;
|
|
use App\Models\Visit;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class CareBillTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected User $user;
|
|
|
|
protected Organization $organization;
|
|
|
|
protected Visit $visit;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$this->user = User::create([
|
|
'public_id' => 'test-user-001',
|
|
'name' => 'Test User',
|
|
'email' => 'test@example.com',
|
|
]);
|
|
|
|
$this->organization = Organization::create([
|
|
'owner_ref' => $this->user->public_id,
|
|
'name' => 'Test Clinic',
|
|
'slug' => 'test-clinic',
|
|
'timezone' => 'UTC',
|
|
'settings' => ['onboarded' => true, 'plan' => 'pro', 'plan_expires_at' => now()->addMonth()->toIso8601String()],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $this->user->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $this->user->public_id,
|
|
'role' => 'cashier',
|
|
]);
|
|
|
|
$branch = Branch::create([
|
|
'owner_ref' => $this->user->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'name' => 'Main Branch',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$patient = Patient::create([
|
|
'uuid' => (string) \Illuminate\Support\Str::uuid(),
|
|
'owner_ref' => $this->user->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_number' => 'LC-2026-00001',
|
|
'first_name' => 'Kofi',
|
|
'last_name' => 'Asante',
|
|
]);
|
|
|
|
$this->visit = Visit::create([
|
|
'uuid' => (string) \Illuminate\Support\Str::uuid(),
|
|
'owner_ref' => $this->user->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => Visit::STATUS_IN_PROGRESS,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
Consultation::create([
|
|
'uuid' => (string) \Illuminate\Support\Str::uuid(),
|
|
'owner_ref' => $this->user->public_id,
|
|
'visit_id' => $this->visit->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => Consultation::STATUS_COMPLETED,
|
|
'started_at' => now()->subHour(),
|
|
'completed_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function test_cashier_can_generate_bill_from_visit(): void
|
|
{
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.generate', $this->visit))
|
|
->assertRedirect();
|
|
|
|
$bill = Bill::first();
|
|
$this->assertNotNull($bill);
|
|
$this->assertSame(Bill::STATUS_OPEN, $bill->status);
|
|
$this->assertTrue($bill->lineItems()->where('type', 'consultation')->exists());
|
|
$this->assertDatabaseHas('care_audit_logs', ['action' => 'bill.created']);
|
|
}
|
|
|
|
public function test_cashier_can_add_line_item_and_record_payments(): void
|
|
{
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.generate', $this->visit));
|
|
|
|
$bill = Bill::firstOrFail();
|
|
$consultationFee = (int) config('care.billing.consultation_fee_minor', 5000);
|
|
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.line-items.store', $bill), [
|
|
'type' => 'misc',
|
|
'description' => 'Dressing supplies',
|
|
'quantity' => 2,
|
|
'unit_price_minor' => 1000,
|
|
])
|
|
->assertRedirect();
|
|
|
|
$bill->refresh();
|
|
$expectedTotal = $consultationFee + 2000;
|
|
|
|
$this->assertSame($expectedTotal, $bill->total_minor);
|
|
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.payments.store', $bill), [
|
|
'amount_minor' => 3000,
|
|
'method' => 'cash',
|
|
])
|
|
->assertRedirect();
|
|
|
|
$bill->refresh();
|
|
$this->assertSame(Bill::STATUS_PARTIAL, $bill->status);
|
|
$this->assertSame(3000, $bill->amount_paid_minor);
|
|
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.payments.store', $bill), [
|
|
'amount_minor' => $bill->balance_minor,
|
|
'method' => 'momo',
|
|
'reference' => 'MOMO-123',
|
|
])
|
|
->assertRedirect();
|
|
|
|
$bill->refresh();
|
|
$this->assertSame(Bill::STATUS_PAID, $bill->status);
|
|
$this->assertSame(0, $bill->balance_minor);
|
|
$this->assertDatabaseHas('care_audit_logs', ['action' => 'payment.recorded']);
|
|
}
|
|
|
|
public function test_bills_index_is_accessible(): void
|
|
{
|
|
$this->actingAs($this->user)
|
|
->post(route('care.bills.generate', $this->visit));
|
|
|
|
$this->actingAs($this->user)
|
|
->get(route('care.bills.index'))
|
|
->assertOk()
|
|
->assertSee('INV-');
|
|
}
|
|
}
|