fix(secretary): allow insurance pages; add grantable subscription resource

Insurance pages redirected to the dashboard: the insurance-pricing/claims
routes never listed `secretary`, so RoleRoute bounced a secretary who had
insurances.view and saw the menu item. Added secretary + permission
['insurances','view'] to both routes; also gated my-financial with
['payments','view'] for consistency.

«خرید اشتراک» was owner-only with no permission toggle, so it could not be
granted. Added a `subscription` secretary resource (view/create) end-to-end:
- entity DEFAULT_PERMISSIONS + SecretaryPermissions type + both secretary forms.
- backend: SubscriptionController::my (view) and trial (create),
  PaymentController::initiateSubscription (create). resolveEntity in
  SubscriptionController was already secretary-aware.
- frontend: subscription + subscription/success routes accept secretary +
  permission; settings navs gate «خرید اشتراک» by ['subscription','view'].

Tests: subscription denied-by-default / allowed-when-granted. docs/api
secretary.md updated (resource list, enforcement map, JSON example).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-23 18:21:53 +03:30
co-authored by Claude Opus 4.8
parent 2fbb7233ef
commit 43db753942
11 changed files with 56 additions and 9 deletions
@@ -35,6 +35,7 @@ class SubscriptionController extends BaseController
private readonly ClinicRepository $clinicRepo,
private readonly UserActiveContextRepository $contextRepo,
private readonly EntityManagerInterface $em,
private readonly \App\Secretary\Security\SecretaryAccessChecker $secretaryAccess,
) {}
// ── Public ──────────────────────────────────────────────────────────────
@@ -56,6 +57,7 @@ class SubscriptionController extends BaseController
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function my(#[CurrentUser] User $user): JsonResponse
{
$this->secretaryAccess->denyUnlessGranted($user, 'subscription', 'view');
[$entityType, $entityId] = $this->resolveEntity($user);
if ($entityId === null) {
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
@@ -76,6 +78,7 @@ class SubscriptionController extends BaseController
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function trial(#[CurrentUser] User $user): JsonResponse
{
$this->secretaryAccess->denyUnlessGranted($user, 'subscription', 'create');
[$entityType, $entityId] = $this->resolveEntity($user);
if ($entityId === null) {
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);