get(\IntlCalendar::FIELD_YEAR), $cal->get(\IntlCalendar::FIELD_MONTH) + 1]; } private function jalaliMonthLength(int $year, int $month): int { $cal = \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian'); $cal->set(\IntlCalendar::FIELD_YEAR, $year); $cal->set(\IntlCalendar::FIELD_MONTH, $month - 1); $cal->set(\IntlCalendar::FIELD_DAY_OF_MONTH, 1); return $cal->getActualMaximum(\IntlCalendar::FIELD_DAY_OF_MONTH); } private function clinicWithDoctor(): array { $clinicOwner = $this->createUser(['ROLE_CLINIC']); $clinic = new Clinic($clinicOwner); $clinic->setName('کلینیک نمودار'); $docOwner = $this->createUser(['ROLE_DOCTOR']); $doctor = new Doctor($docOwner, 'حمیدی'); $clinic->getDoctors()->add($doctor); $this->em->persist($doctor); $this->em->persist($clinic); $this->em->flush(); return [$clinicOwner, $doctor]; } public function testClinicChartsDefaultToCurrentJalaliPeriod(): void { [$owner] = $this->clinicWithDoctor(); [$year, $month] = $this->currentJalali(); $res = $this->authJson('GET', '/api/v1/dashboard/clinic', $owner); self::assertSame(200, $this->responseCode()); self::assertSame( ['patients_year' => $year, 'patients_month' => $month, 'revenue_year' => $year], $res['data']['charts_period'] ); self::assertCount($this->jalaliMonthLength($year, $month), $res['data']['charts']['appointments_by_day']); self::assertCount(12, $res['data']['charts']['revenue_by_month']); } public function testClinicChartsHonourRequestedPeriod(): void { [$owner] = $this->clinicWithDoctor(); // اسفند ۱۴۰۳ سال کبیسه است → ۳۰ روز $res = $this->authJson('GET', '/api/v1/dashboard/clinic?patients_year=1403&patients_month=12&revenue_year=1402', $owner); self::assertSame(200, $this->responseCode()); self::assertSame( ['patients_year' => 1403, 'patients_month' => 12, 'revenue_year' => 1402], $res['data']['charts_period'] ); self::assertCount(30, $res['data']['charts']['appointments_by_day']); } public function testTodayAppointmentIsCountedOnItsJalaliDay(): void { [$owner, $doctor] = $this->clinicWithDoctor(); $patient = $this->createUser(['ROLE_USER']); $start = time(); $this->em->persist($this->newAppointment($doctor, $patient, $start, $start + 1_800)); $this->em->flush(); $res = $this->authJson('GET', '/api/v1/dashboard/clinic', $owner); $days = $res['data']['charts']['appointments_by_day']; $cal = \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian'); $todayIndex = $cal->get(\IntlCalendar::FIELD_DAY_OF_MONTH) - 1; self::assertGreaterThanOrEqual(1, $days[$todayIndex]['count'], 'today\'s slot must land on today\'s bar'); } public function testOutOfRangeParamsAreClamped(): void { [$owner] = $this->clinicWithDoctor(); [$year] = $this->currentJalali(); $res = $this->authJson('GET', '/api/v1/dashboard/clinic?patients_month=99&patients_year=9999&revenue_year=0', $owner); self::assertSame(200, $this->responseCode()); $period = $res['data']['charts_period']; self::assertSame(12, $period['patients_month']); self::assertSame(1500, $period['patients_year']); // revenue_year=0 is falsy → falls back to the current Jalali year self::assertSame($year, $period['revenue_year']); } public function testDoctorChartsUseSameJalaliPeriod(): void { $owner = $this->createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'حمیدی'); $this->em->persist($doctor); $this->em->flush(); $res = $this->authJson('GET', '/api/v1/dashboard/doctor?patients_year=1403&patients_month=1', $owner); self::assertSame(200, $this->responseCode()); self::assertSame(1403, $res['data']['charts_period']['patients_year']); self::assertCount(31, $res['data']['charts']['appointments_by_day']); } }