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:
hamed
2026-07-25 18:34:18 +03:30
parent 73a608c3dd
commit 8d2b0d908a
33 changed files with 2564 additions and 76 deletions
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace App\Shared\Entity;
use Symfony\Component\Uid\Uuid;
/**
* شماره‌های شبای یک موجودیت (نماینده، پروفایل کاربر، …) روی ستون JSON `bank_account`.
* حداکثر دو شبا؛ `verified` فقط از سمت ادمین ست می‌شود و تسویه تنها با شبای تأییدشده
* انجام می‌گیرد. استفاده‌کننده باید ستون `bank_account` و متد `touch()` را داشته باشد.
*/
trait HasIbansTrait
{
/** @return array<int, array<string, mixed>> */
public function getIbans(): array
{
return $this->bankAccount ?? [];
}
/**
* @param array{iban: string, bank_name?: ?string, owner_name?: ?string, verified?: bool} $iban
* @throws \DomainException `iban_limit` وقتی از سقف دو شبا بگذرد
*/
public function addIban(array $iban): static
{
$ibans = $this->getIbans();
if (count($ibans) >= 2) {
throw new \DomainException('iban_limit');
}
$ibans[] = [
'id' => Uuid::v4()->toRfc4122(),
'iban' => $iban['iban'],
'bank_name' => $iban['bank_name'] ?? null,
'owner_name' => $iban['owner_name'] ?? null,
'verified' => $iban['verified'] ?? false,
'created_at' => time(),
];
$this->bankAccount = $ibans;
$this->touch();
return $this;
}
public function removeIban(string $id): static
{
$this->bankAccount = array_values(array_filter(
$this->getIbans(),
static fn(array $i) => ($i['id'] ?? null) !== $id,
));
$this->touch();
return $this;
}
/** @return array<string, mixed>|null یک شبای تأییدشده با این id */
public function findVerifiedIban(string $id): ?array
{
foreach ($this->getIbans() as $iban) {
if (($iban['id'] ?? null) === $id && ($iban['verified'] ?? false)) {
return $iban;
}
}
return null;
}
}