fix financial issues
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\View {
|
||||
if (!function_exists(__NAMESPACE__ . '\view')) {
|
||||
function view($name, array $data = [], $options = [])
|
||||
{
|
||||
return ['view' => $name, 'data' => $data, 'options' => $options];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Tests\App\Controllers\View {
|
||||
|
||||
use App\Controllers\View\TuitionForecastController;
|
||||
use App\Libraries\Tuition\TuitionForecastService;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
|
||||
class ForecastRequestStub
|
||||
{
|
||||
public function __construct(
|
||||
private array $get = [],
|
||||
private array $post = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function getGet(?string $key = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->get;
|
||||
}
|
||||
|
||||
return $this->get[$key] ?? null;
|
||||
}
|
||||
|
||||
public function getPost(?string $key = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
return $this->post[$key] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeForecastService extends TuitionForecastService
|
||||
{
|
||||
public array $calls = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function calculate(string $schoolYear = '', string $semester = '', string $mode = 'compare', array $options = []): array
|
||||
{
|
||||
$this->calls[] = compact('schoolYear', 'semester', 'mode', 'options');
|
||||
|
||||
return [
|
||||
'school_year' => $schoolYear !== '' ? $schoolYear : '2025-2026',
|
||||
'semester' => $semester,
|
||||
'calculator_mode' => $mode,
|
||||
'options' => [
|
||||
'include_withdrawn_mode' => $options['include_withdrawn_mode'] ?? 'refund_deadline',
|
||||
'include_payment_pending' => $options['include_payment_pending'] ?? '0',
|
||||
'include_event_only' => $options['include_event_only'] ?? '0',
|
||||
'include_paid_invoices' => $options['include_paid_invoices'] ?? '0',
|
||||
'unit_price' => $options['unit_price'] ?? '370.00',
|
||||
],
|
||||
'summary' => [
|
||||
'family_count' => 1,
|
||||
'student_count' => 2,
|
||||
'billable_student_count' => 2,
|
||||
'old_projected_tuition' => '550.00',
|
||||
'new_projected_tuition' => '650.00',
|
||||
'old_projected_income' => '550.00',
|
||||
'new_projected_income' => '650.00',
|
||||
'unit_price' => '370.00',
|
||||
'difference' => '100.00',
|
||||
],
|
||||
'families' => [[
|
||||
'parent_name' => 'Layla Yusuf',
|
||||
'student_count' => 2,
|
||||
'billable_student_count' => 2,
|
||||
'old_total' => '550.00',
|
||||
'new_total' => '650.00',
|
||||
'difference' => '100.00',
|
||||
'warnings' => ['warning'],
|
||||
'student_details' => [[
|
||||
'student_name' => 'Student One',
|
||||
'grade_level' => '1',
|
||||
'billable' => true,
|
||||
'excluded_reason' => '',
|
||||
'old_rule' => 'full',
|
||||
'old_amount' => '350.00',
|
||||
'new_rule' => 'full',
|
||||
'new_amount' => '370.00',
|
||||
]],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
public function getAvailableSchoolYears(): array
|
||||
{
|
||||
return ['2025-2026'];
|
||||
}
|
||||
|
||||
public function getAvailableSemesters(): array
|
||||
{
|
||||
return ['Fall', 'Spring'];
|
||||
}
|
||||
}
|
||||
|
||||
class TestableTuitionForecastController extends TuitionForecastController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function setForecastService(TuitionForecastService $service): self
|
||||
{
|
||||
$this->forecastService = $service;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequestObject($request): self
|
||||
{
|
||||
$this->request = $request;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setResponseObject(ResponseInterface $response): self
|
||||
{
|
||||
$this->response = $response;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class TuitionForecastControllerTest extends CIUnitTestCase
|
||||
{
|
||||
private TestableTuitionForecastController $controller;
|
||||
private FakeForecastService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->service = new FakeForecastService();
|
||||
$this->controller = (new TestableTuitionForecastController())
|
||||
->setForecastService($this->service)
|
||||
->setResponseObject(service('response'));
|
||||
}
|
||||
|
||||
public function testIndexBuildsForecastViewDataFromQueryParameters(): void
|
||||
{
|
||||
$request = new ForecastRequestStub([
|
||||
'school_year' => '2026-2027',
|
||||
'semester' => 'Spring',
|
||||
'calculator_mode' => 'new',
|
||||
'include_withdrawn_mode' => 'always',
|
||||
'include_payment_pending' => '1',
|
||||
'include_event_only' => '1',
|
||||
'include_paid_invoices' => '1',
|
||||
'unit_price' => '395.00',
|
||||
]);
|
||||
|
||||
$result = $this->controller->setRequestObject($request)->index();
|
||||
|
||||
$this->assertSame('administrator/tuition_forecast', $result['view']);
|
||||
$this->assertSame('2026-2027', $result['data']['filters']['school_year']);
|
||||
$this->assertSame('Spring', $result['data']['filters']['semester']);
|
||||
$this->assertSame('new', $result['data']['filters']['calculator_mode']);
|
||||
$this->assertSame('1', $result['data']['filters']['include_payment_pending']);
|
||||
$this->assertSame('395.00', $result['data']['filters']['unit_price']);
|
||||
$this->assertSame('always', $this->service->calls[0]['options']['include_withdrawn_mode']);
|
||||
}
|
||||
|
||||
public function testCalculateReturnsJsonPayload(): void
|
||||
{
|
||||
$request = new ForecastRequestStub([], [
|
||||
'school_year' => '2025-2026',
|
||||
'semester' => 'Fall',
|
||||
'calculator_mode' => 'compare',
|
||||
'include_withdrawn_mode' => 'refund_deadline',
|
||||
'include_payment_pending' => '1',
|
||||
'include_event_only' => '0',
|
||||
'include_paid_invoices' => '1',
|
||||
'unit_price' => '390.00',
|
||||
]);
|
||||
|
||||
$response = $this->controller->setRequestObject($request)->calculate();
|
||||
$payload = json_decode((string) $response->getBody(), true);
|
||||
|
||||
$this->assertSame('2025-2026', $payload['school_year']);
|
||||
$this->assertSame('Fall', $payload['semester']);
|
||||
$this->assertSame('compare', $payload['calculator_mode']);
|
||||
$this->assertSame('390.00', $payload['options']['unit_price']);
|
||||
}
|
||||
|
||||
public function testExportCsvBuildsDownloadWithSummaryAndFamilyRows(): void
|
||||
{
|
||||
$request = new ForecastRequestStub([
|
||||
'school_year' => '2025-2026',
|
||||
'calculator_mode' => 'compare',
|
||||
]);
|
||||
|
||||
$response = $this->controller->setRequestObject($request)->exportCsv();
|
||||
$body = (string) $response->getBody();
|
||||
|
||||
$this->assertStringContainsString('text/csv', $response->getHeaderLine('Content-Type'));
|
||||
$this->assertStringContainsString('tuition_forecast_2025-2026_all-year_compare.csv', $response->getHeaderLine('Content-Disposition'));
|
||||
$this->assertStringContainsString('Summary', $body);
|
||||
$this->assertStringContainsString('All Year', $body);
|
||||
$this->assertStringContainsString('Old Projected Income', $body);
|
||||
$this->assertStringContainsString('Layla Yusuf', $body);
|
||||
$this->assertStringContainsString('Student One', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user