feat: Add online share functionality for secretaries
- Introduced `online_share_enabled` and `online_share_percent` fields in the `doctor_secretaries` table to manage secretary shares from online appointments. - Added `bank_account` field in the `profiles` table to store user-level IBANs for settlements. - Created `secretary_earnings` table to track earnings per secretary from online appointments, including a foreign key relationship with `financial_breakdowns`. - Implemented `SecretaryEarning` entity and repository for managing secretary earnings. - Developed `SecretaryShareResolver` service to determine which secretaries earn from online payments. - Added `UserIbanResolver` service to handle user IBAN retrieval and management. - Created `HasIbansTrait` for entities to manage IBANs in a JSON format. - Implemented tests for secretary earnings and API endpoints for managing secretary shares and IBANs.
This commit is contained in:
@@ -46,6 +46,8 @@ class AdminApiController extends BaseController
|
||||
private readonly \App\Patient\Service\PatientResolver $patientResolver,
|
||||
private readonly \App\Insurance\Service\VisitPriceRequirementResolver $visitPriceResolver,
|
||||
private readonly \App\Appointment\Service\AppointmentConfirmationService $appointmentConfirmation,
|
||||
private readonly \App\Secretary\Repository\DoctorSecretaryRepository $doctorSecretaryRepo,
|
||||
private readonly \App\Secretary\Repository\SecretaryEarningRepository $secretaryEarningRepo,
|
||||
) {}
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────────────────
|
||||
@@ -1500,6 +1502,7 @@ class AdminApiController extends BaseController
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->select(
|
||||
'ds.uuid, ds.permissions, ds.active, ds.createdAt',
|
||||
'ds.onlineShareEnabled, ds.onlineSharePercent',
|
||||
'u.mobileNumber as mobile, u.realName as user_name',
|
||||
'd.name as doctor_name, d.uuid as doctor_uuid',
|
||||
)
|
||||
@@ -1525,6 +1528,8 @@ class AdminApiController extends BaseController
|
||||
'doctor_name' => $ds['doctor_name'],
|
||||
'doctor_uuid' => $ds['doctor_uuid'],
|
||||
'is_active' => (bool) $ds['active'],
|
||||
'online_share_enabled' => (bool) $ds['onlineShareEnabled'],
|
||||
'online_share_percent' => (float) $ds['onlineSharePercent'],
|
||||
'permissions' => $ds['permissions'] ?? DoctorSecretary::DEFAULT_PERMISSIONS,
|
||||
'created_at' => date('c', (int) $ds['createdAt']),
|
||||
], $rows);
|
||||
@@ -1532,6 +1537,75 @@ class AdminApiController extends BaseController
|
||||
return $this->paginated($items, (int) $total, $page, $limit);
|
||||
}
|
||||
|
||||
// ── Secretary detail + online-appointment share ───────────────────────────
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/admin/secretary/{uuid}',
|
||||
summary: 'Secretary relation detail with its online-appointment share settings and earnings',
|
||||
security: [['bearerAuth' => []]],
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'Secretary detail'),
|
||||
new OA\Response(response: 404, description: 'Secretary not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/admin/secretary/{uuid}', methods: ['GET'])]
|
||||
public function secretaryDetail(string $uuid): JsonResponse
|
||||
{
|
||||
$relation = $this->doctorSecretaryRepo->findByUuid($uuid);
|
||||
if ($relation === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'منشی یافت نشد', 404);
|
||||
}
|
||||
|
||||
$secretary = $relation->getSecretary();
|
||||
$monthStart = time() - 30 * 86_400;
|
||||
|
||||
return $this->success([
|
||||
'data' => $relation->toArray() + [
|
||||
'clinic_name' => $relation->getClinic()?->getName(),
|
||||
'earnings' => [
|
||||
'total_rials' => $this->secretaryEarningRepo->sumFor($secretary),
|
||||
'this_month_rials' => $this->secretaryEarningRepo->sumFor($secretary, $monthStart),
|
||||
'appointments_count' => $this->secretaryEarningRepo->countFor($secretary),
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/api/v1/admin/secretary/{uuid}/online-share',
|
||||
summary: 'Enable/disable the secretary share of online appointments and set its percent',
|
||||
security: [['bearerAuth' => []]],
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'Saved'),
|
||||
new OA\Response(response: 404, description: 'Secretary not found'),
|
||||
new OA\Response(response: 422, description: 'Invalid percent'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/admin/secretary/{uuid}/online-share', methods: ['PUT'])]
|
||||
public function saveSecretaryOnlineShare(string $uuid, Request $request): JsonResponse
|
||||
{
|
||||
$relation = $this->doctorSecretaryRepo->findByUuid($uuid);
|
||||
if ($relation === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'منشی یافت نشد', 404);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$enabled = (bool) ($data['enabled'] ?? false);
|
||||
$percent = (float) ($data['percent'] ?? 0);
|
||||
|
||||
if ($percent < 0 || $percent > 100) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'درصد سهم باید بین ۰ تا ۱۰۰ باشد', 422, 'percent');
|
||||
}
|
||||
if ($enabled && $percent <= 0) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'برای فعالسازی، درصد سهم باید بیشتر از صفر باشد', 422, 'percent');
|
||||
}
|
||||
|
||||
$relation->setOnlineShareEnabled($enabled)->setOnlineSharePercent($percent);
|
||||
$this->doctorSecretaryRepo->save($relation);
|
||||
|
||||
return $this->success(['data' => $relation->toArray()]);
|
||||
}
|
||||
|
||||
// ── Ratings ───────────────────────────────────────────────────────────────
|
||||
|
||||
#[OA\Get(
|
||||
|
||||
Reference in New Issue
Block a user