createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'دکتر تست'); $this->em->persist($doctor); $this->em->flush(); return [$owner, $doctor]; } private function patientRecord(Doctor $doctor, ?string $nationalCode = null): PatientRecord { $patient = $this->createUser(['ROLE_USER']); if ($nationalCode !== null) { $patient->setNationalCode($nationalCode); } $record = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId()); $this->em->persist($record); $this->em->flush(); return $record; } private function setField(object $obj, string $prop, mixed $value): void { $ref = new \ReflectionProperty($obj, $prop); $ref->setAccessible(true); $ref->setValue($obj, $value); } private function invoice(Doctor $doctor, PatientRecord $record, int $patientRials, string $status, int $issuedAt): Invoice { $invoice = new Invoice('doctor', $doctor->getId()); $invoice->setPatientRecordId($record->getId()); $this->setField($invoice, 'patientRials', $patientRials); $this->setField($invoice, 'status', $status); $this->setField($invoice, 'issuedAt', $issuedAt); $this->em->persist($invoice); $this->em->flush(); return $invoice; } public function testSummarySplitsPaidAndUnsettled(): void { [$owner, $doctor] = $this->doctor(); $record = $this->patientRecord($doctor); $this->invoice($doctor, $record, 1_000_000, Invoice::STATUS_PAID, 1_700_000_000); $this->invoice($doctor, $record, 400_000, Invoice::STATUS_FINALIZED, 1_700_000_100); // draft invoices are not part of the payments list, so they must not count $this->invoice($doctor, $record, 999_000, Invoice::STATUS_DRAFT, 1_700_000_200); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary', $owner); self::assertSame(200, $this->responseCode()); $data = $res['data']; self::assertSame(1_400_000, $data['total_rials']); self::assertSame(1_000_000, $data['paid_rials']); self::assertSame(400_000, $data['unsettled_rials']); self::assertSame(2, $data['invoices_count']); } public function testSummaryHonoursStatusAndDateFilters(): void { [$owner, $doctor] = $this->doctor(); $record = $this->patientRecord($doctor); $this->invoice($doctor, $record, 1_000_000, Invoice::STATUS_PAID, 1_700_000_000); $this->invoice($doctor, $record, 400_000, Invoice::STATUS_FINALIZED, 1_700_000_000); $this->invoice($doctor, $record, 700_000, Invoice::STATUS_PAID, 1_800_000_000); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary?status=paid', $owner); self::assertSame(1_700_000, $res['data']['total_rials']); self::assertSame(1_700_000, $res['data']['paid_rials']); self::assertSame(0, $res['data']['unsettled_rials']); self::assertSame(2, $res['data']['invoices_count']); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary?from=1700000000&to=1700000001', $owner); self::assertSame(1_400_000, $res['data']['total_rials']); self::assertSame(2, $res['data']['invoices_count']); } public function testSummaryFiltersByNationalCode(): void { [$owner, $doctor] = $this->doctor(); // db_test is never reset — a fixed code would eventually collide on the unique column $code = (string) random_int(1_000_000_000, 9_999_999_999); $mine = $this->patientRecord($doctor, $code); $other = $this->patientRecord($doctor, (string) random_int(1_000_000_000, 9_999_999_999)); $this->invoice($doctor, $mine, 500_000, Invoice::STATUS_PAID, 1_700_000_000); $this->invoice($doctor, $other, 800_000, Invoice::STATUS_PAID, 1_700_000_000); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary?national_code=' . $code, $owner); self::assertSame(500_000, $res['data']['total_rials']); self::assertSame(1, $res['data']['invoices_count']); } public function testSummaryIsZeroWhenNothingMatches(): void { [$owner] = $this->doctor(); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary', $owner); self::assertSame(200, $this->responseCode()); self::assertSame( ['total_rials' => 0, 'paid_rials' => 0, 'unsettled_rials' => 0, 'invoices_count' => 0], $res['data'], ); } public function testSummaryExcludesOtherTenants(): void { [$owner, $doctor] = $this->doctor(); $record = $this->patientRecord($doctor); $this->invoice($doctor, $record, 300_000, Invoice::STATUS_PAID, 1_700_000_000); [$otherOwner] = $this->doctor(); $res = $this->authJson('GET', '/api/v1/my/billing/payments/summary', $otherOwner); self::assertSame(0, $res['data']['total_rials']); self::assertSame(0, $res['data']['invoices_count']); } }