createUser(['ROLE_USER', 'ROLE_DOCTOR']); $doctor = new Doctor($doctorUser, 'دکتر تست API سهم'); $this->em->persist($doctor); $secretaryUser = $this->createUser(['ROLE_USER', 'ROLE_SECRETARY']); $relation = new DoctorSecretary($doctor, $secretaryUser); $this->em->persist($relation); $this->em->flush(); return [$relation, $secretaryUser]; } // ── ادمین ──────────────────────────────────────────────────────────────── public function testAdminSeesTheRelationDetailWithEarnings(): void { [$relation] = $this->makeRelation(); $admin = $this->createUser(['ROLE_ADMIN']); $body = $this->authJson('GET', '/api/v1/admin/secretary/' . $relation->getUuid(), $admin); self::assertSame(200, $this->responseCode()); $row = $body['data']['data']; self::assertFalse($row['online_share_enabled']); // JSON عددِ گِرد را بدون ممیز می‌دهد، پس مقایسه‌ی نوع‌آزاد. self::assertEquals(0, $row['online_share_percent']); self::assertSame(0, $row['earnings']['total_rials']); } public function testAdminEnablesTheShareWithAPercent(): void { [$relation] = $this->makeRelation(); $admin = $this->createUser(['ROLE_ADMIN']); $body = $this->authJson('PUT', '/api/v1/admin/secretary/' . $relation->getUuid() . '/online-share', $admin, [ 'enabled' => true, 'percent' => 5, ]); self::assertSame(200, $this->responseCode()); self::assertTrue($body['data']['data']['online_share_enabled']); self::assertEquals(5, $body['data']['data']['online_share_percent']); } public function testPercentAbove100IsRejected(): void { [$relation] = $this->makeRelation(); $admin = $this->createUser(['ROLE_ADMIN']); $this->authJson('PUT', '/api/v1/admin/secretary/' . $relation->getUuid() . '/online-share', $admin, [ 'enabled' => true, 'percent' => 101, ]); self::assertSame(422, $this->responseCode()); } public function testEnablingWithoutAPercentIsRejected(): void { [$relation] = $this->makeRelation(); $admin = $this->createUser(['ROLE_ADMIN']); $this->authJson('PUT', '/api/v1/admin/secretary/' . $relation->getUuid() . '/online-share', $admin, [ 'enabled' => true, 'percent' => 0, ]); self::assertSame(422, $this->responseCode()); } public function testNonAdminIsForbidden(): void { [$relation, $secretaryUser] = $this->makeRelation(); $this->authJson('GET', '/api/v1/admin/secretary/' . $relation->getUuid(), $secretaryUser); self::assertSame(403, $this->responseCode()); } public function testUnknownRelationIsNotFound(): void { $admin = $this->createUser(['ROLE_ADMIN']); $this->authJson('GET', '/api/v1/admin/secretary/00000000-0000-4000-8000-000000000000', $admin); self::assertSame(404, $this->responseCode()); } // ── پنل منشی ───────────────────────────────────────────────────────────── public function testSummaryReportsDisabledWhenNoRelationHasAShare(): void { [, $secretaryUser] = $this->makeRelation(); $body = $this->authJson('GET', '/api/v1/secretary/earnings/summary', $secretaryUser); self::assertSame(200, $this->responseCode()); self::assertFalse($body['data']['data']['enabled']); self::assertSame(0, $body['data']['data']['total_rials']); } public function testSummaryReportsEnabledWithThePercentOnceAdminTurnsItOn(): void { [$relation, $secretaryUser] = $this->makeRelation(); $relation->setOnlineShareEnabled(true)->setOnlineSharePercent(7.5); $this->em->flush(); $body = $this->authJson('GET', '/api/v1/secretary/earnings/summary', $secretaryUser); self::assertSame(200, $this->responseCode()); self::assertTrue($body['data']['data']['enabled']); self::assertSame(7.5, $body['data']['data']['share_percent']); } public function testReportIsEmptyButSucceedsWithoutEarnings(): void { [, $secretaryUser] = $this->makeRelation(); $body = $this->authJson('GET', '/api/v1/secretary/earnings/report', $secretaryUser); self::assertSame(200, $this->responseCode()); self::assertSame([], $body['data']); self::assertSame(0, $body['meta']['totalRecords']); } public function testSecretaryManagesItsIbans(): void { [, $secretaryUser] = $this->makeRelation(); $added = $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, [ 'iban' => 'IR' . str_pad((string) random_int(0, 999_999), 24, '0', STR_PAD_LEFT), 'bank_name' => 'ملی', ]); self::assertSame(201, $this->responseCode()); $ibans = $added['data']['data']['bank_account']; self::assertCount(1, $ibans); // شبای تازه تأییدنشده است؛ تسویه با آن مجاز نیست. self::assertFalse($ibans[0]['verified']); $removed = $this->authJson('DELETE', '/api/v1/secretary/iban/' . $ibans[0]['id'], $secretaryUser); self::assertSame(200, $this->responseCode()); self::assertSame([], $removed['data']['data']['bank_account']); } public function testInvalidIbanIsRejected(): void { [, $secretaryUser] = $this->makeRelation(); $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, ['iban' => '12345']); self::assertSame(422, $this->responseCode()); } public function testThirdIbanIsRejected(): void { [, $secretaryUser] = $this->makeRelation(); $iban = fn() => 'IR' . str_pad((string) random_int(0, 999_999_999), 24, '0', STR_PAD_LEFT); $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, ['iban' => $iban()]); $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, ['iban' => $iban()]); $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, ['iban' => $iban()]); self::assertSame(422, $this->responseCode()); } public function testSettlementAcceptsAVerifiedSecretaryIban(): void { [, $secretaryUser] = $this->makeRelation(); $added = $this->authJson('POST', '/api/v1/secretary/iban', $secretaryUser, [ 'iban' => 'IR' . str_pad((string) random_int(0, 999_999), 24, '0', STR_PAD_LEFT), ]); $ibanId = $added['data']['data']['bank_account'][0]['id']; // تأیید شبا کارِ ادمین است؛ اینجا مستقیم روی پروفایل ست می‌کنیم. $profile = static::getContainer()->get(\App\UserProfile\Repository\UserProfileRepository::class) ->findByUser($secretaryUser); $ibans = $profile->getIbans(); $ibans[0]['verified'] = true; $profile->setBankAccount($ibans); $this->em->flush(); // بدون موجودی: خطای موجودی می‌گیرد، نه خطای شبا — یعنی شبا پذیرفته شده است. $body = $this->authJson('POST', '/api/v1/settlement', $secretaryUser, [ 'amount_rials' => 100_000, 'iban_id' => $ibanId, ]); self::assertSame(422, $this->responseCode()); self::assertSame('موجودی کافی نیست', $body['errors'][0]['message']); } }