Fix semester context, attendance rosters, and billing workflows
- load global semester helpers consistently and use date-based semester defaults - fix grading and daily attendance duplicate student/section rows - keep attendance violations scoped to the current semester by default - update invoice, refund, discount, payment, and financial aid flows - add configuration cleanup migrations for duplicate calendar/semester keys - refresh parent registration/report-card and print request handling - update related models, services, views, cron notes, and test coverage
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace {
|
||||
if (!function_exists('site_url')) {
|
||||
function site_url($uri = '')
|
||||
{
|
||||
return 'https://test.alrahmaisgl.org/' . ltrim((string) $uri, '/');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('view')) {
|
||||
function view($name, $data = [], $options = [])
|
||||
{
|
||||
return ['view' => $name, 'data' => $data, 'options' => $options];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Tests\App\Controllers\View {
|
||||
|
||||
use App\Controllers\View\ExpenseController;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
use Config\Services;
|
||||
|
||||
class TestableExpenseController extends ExpenseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Skip the real constructor so tests can inject fakes.
|
||||
}
|
||||
|
||||
public function setExpenseModel(object $model): self
|
||||
{
|
||||
$this->expenseModel = $model;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setConfigModel(object $model): self
|
||||
{
|
||||
$this->configModel = $model;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpenseIndexFakeModel
|
||||
{
|
||||
public array $whereCalls = [];
|
||||
|
||||
public function select(string $select): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function join(string $table, string $condition, string $type = ''): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where(string $field, mixed $value): self
|
||||
{
|
||||
$this->whereCalls[] = [$field, $value];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function orderBy(string $field, string $direction = ''): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function findAll(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'id' => 10,
|
||||
'category' => 'Expense',
|
||||
'amount' => '12.50',
|
||||
'receipt_path' => 'receipt.pdf',
|
||||
'school_year' => '2025-2026',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class ExpenseIndexFakeConfig
|
||||
{
|
||||
public function getConfig(string $key): ?string
|
||||
{
|
||||
return $key === 'school_year' ? '2025-2026' : null;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpenseFakeRenderer
|
||||
{
|
||||
private array $data = [];
|
||||
private array $lastRender = [];
|
||||
|
||||
public function setData(?array $data = null): self
|
||||
{
|
||||
$this->data = $data ?? [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render(string $view, array $data = [], $options = null)
|
||||
{
|
||||
$this->lastRender = [
|
||||
'view' => $view,
|
||||
'data' => $data ?: $this->data,
|
||||
'options' => $options,
|
||||
];
|
||||
|
||||
return 'fake-rendered:' . $view;
|
||||
}
|
||||
|
||||
public function getLastRender(): array
|
||||
{
|
||||
return $this->lastRender;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpenseControllerTest extends CIUnitTestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Services::resetSingle('renderer');
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testIndexScopesExpensesToActiveConfiguredSchoolYear(): void
|
||||
{
|
||||
$expenseModel = new ExpenseIndexFakeModel();
|
||||
$renderer = new ExpenseFakeRenderer();
|
||||
Services::injectMock('renderer', $renderer);
|
||||
|
||||
$controller = (new TestableExpenseController())
|
||||
->setExpenseModel($expenseModel)
|
||||
->setConfigModel(new ExpenseIndexFakeConfig());
|
||||
|
||||
$result = $controller->index();
|
||||
$this->assertSame('fake-rendered:expenses/index', $result);
|
||||
$rendered = $renderer->getLastRender();
|
||||
|
||||
$this->assertSame('expenses/index', $rendered['view']);
|
||||
$this->assertSame('2025-2026', $rendered['data']['schoolYear']);
|
||||
$this->assertSame([
|
||||
['expenses.school_year', '2025-2026'],
|
||||
], $expenseModel->whereCalls);
|
||||
$this->assertSame(
|
||||
site_url('receipts/receipt.pdf'),
|
||||
$rendered['data']['expenses'][0]['receipt_url']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user