id(); $table->string('firstname')->nullable(); $table->string('lastname')->nullable(); }); Schema::create('invoices', function (Blueprint $table) { $table->id(); $table->string('invoice_number')->nullable(); }); Schema::create('additional_charges', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->nullable(); $table->unsignedBigInteger('invoice_id')->nullable(); $table->string('school_year', 20); $table->string('semester', 20); $table->string('charge_type', 20); $table->string('title'); $table->text('description')->nullable(); $table->decimal('amount', 10, 2); $table->date('due_date')->nullable(); $table->string('status', 20); $table->unsignedBigInteger('created_by')->nullable(); $table->timestamps(); }); } public function test_scope_by_parent_term_filters_status_and_orders_desc(): void { $parentId = 1; AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => null, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Older', 'description' => null, 'amount' => 10.00, 'due_date' => '2024-09-01', 'status' => 'pending', 'created_by' => null, ]); $latest = AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => null, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Latest', 'description' => null, 'amount' => 20.00, 'due_date' => '2024-10-01', 'status' => 'pending', 'created_by' => null, ]); AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => null, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Applied', 'description' => null, 'amount' => 5.00, 'due_date' => '2024-08-01', 'status' => 'applied', 'created_by' => null, ]); $results = AdditionalCharge::query() ->byParentTerm($parentId, '2024-2025', 'Fall', 'pending') ->get(); $this->assertCount(2, $results); $this->assertSame($latest->id, $results->first()->id); $this->assertSame('pending', $results->first()->status); } public function test_mark_applied_updates_status_and_invoice(): void { $invoiceId = 1; $first = AdditionalCharge::create([ 'parent_id' => 1, 'invoice_id' => null, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Charge 1', 'description' => null, 'amount' => 15.00, 'due_date' => null, 'status' => 'pending', 'created_by' => null, ]); $second = AdditionalCharge::create([ 'parent_id' => 1, 'invoice_id' => null, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Charge 2', 'description' => null, 'amount' => 25.00, 'due_date' => null, 'status' => 'pending', 'created_by' => null, ]); $model = new AdditionalCharge; $result = $model->markApplied([$first->id, (string) $second->id], $invoiceId); $this->assertTrue($result); $this->assertSame('applied', $first->refresh()->status); $this->assertSame($invoiceId, $first->invoice_id); $this->assertSame('applied', $second->refresh()->status); $this->assertSame($invoiceId, $second->invoice_id); } public function test_list_all_for_term_falls_back_to_year(): void { $parentId = 1; $invoiceId = 1; DB::table('users')->insert([ 'id' => $parentId, 'firstname' => 'Sara', 'lastname' => 'Khan', ]); DB::table('invoices')->insert([ 'id' => $invoiceId, 'invoice_number' => 'INV-1', ]); AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => $invoiceId, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Fee', 'description' => null, 'amount' => 30.00, 'due_date' => null, 'status' => 'pending', 'created_by' => null, ]); $paginator = (new AdditionalCharge)->listAllForTerm('2024-2025', 'Spring', null, null, 50, true); $this->assertSame(1, $paginator->total()); $first = $paginator->items()[0]; $this->assertSame('Khan, Sara', $first->parent_name); $this->assertSame('INV-1', $first->invoice_number); } public function test_list_all_for_term_filters_by_search_query(): void { $parentId = 1; $invoiceId = 1; DB::table('users')->insert([ 'id' => $parentId, 'firstname' => 'Amina', 'lastname' => 'Hassan', ]); DB::table('invoices')->insert([ 'id' => $invoiceId, 'invoice_number' => 'INV-2025-001', ]); AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => $invoiceId, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Math Club Fee', 'description' => 'After school enrichment', 'amount' => 30.00, 'due_date' => null, 'status' => 'pending', 'created_by' => null, ]); AdditionalCharge::create([ 'parent_id' => $parentId, 'invoice_id' => $invoiceId, 'school_year' => '2024-2025', 'semester' => 'Fall', 'charge_type' => 'extra', 'title' => 'Science Lab', 'description' => 'Lab supplies', 'amount' => 20.00, 'due_date' => null, 'status' => 'pending', 'created_by' => null, ]); $paginator = (new AdditionalCharge)->listAllForTerm('2024-2025', 'Fall', null, 'Math', 50, true); $this->assertSame(1, $paginator->total()); $this->assertSame('Math Club Fee', $paginator->items()[0]->title); $byDescription = (new AdditionalCharge)->listAllForTerm('2024-2025', 'Fall', null, 'enrichment', 50, true); $this->assertSame(1, $byDescription->total()); $this->assertSame('Math Club Fee', $byDescription->items()[0]->title); $byParentName = (new AdditionalCharge)->listAllForTerm('2024-2025', 'Fall', null, 'Hassan', 50, true); $this->assertSame(2, $byParentName->total()); $byInvoice = (new AdditionalCharge)->listAllForTerm('2024-2025', 'Fall', null, 'INV-2025-001', 50, true); $this->assertSame(2, $byInvoice->total()); } }