[ 'name' => 'Базовый модуль', 'description' => 'Основные функции', 'price_monthly' => 0, 'price_yearly' => 0, 'trial_days' => 0, ], 'crm' => [ 'name' => 'CRM', 'description' => 'Управление клиентами и сделками', 'price_monthly' => 990, 'price_yearly' => 9900, 'trial_days' => 14, ], 'booking' => [ 'name' => 'Бронирования', 'description' => 'Управление бронированиями', 'price_monthly' => 1490, 'price_yearly' => 14900, 'trial_days' => 14, ], 'tasks' => [ 'name' => 'Задачи', 'description' => 'Управление задачами', 'price_monthly' => 790, 'price_yearly' => 7900, 'trial_days' => 14, ], 'proof' => [ 'name' => 'Proof', 'description' => 'Согласование документов', 'price_monthly' => 590, 'price_yearly' => 5900, 'trial_days' => 14, ], ]; protected $db; protected string $moduleSettingsTable = 'module_settings'; protected string $subscriptionsTable = 'organization_subscriptions'; public function __construct() { $this->db = \Config\Database::connect(); } /** * Получение базовой конфигурации модуля */ public function getModuleConfig(string $moduleCode): ?array { return $this->modulesConfig[$moduleCode] ?? null; } /** * Получение всех модулей (с учётом переопределений из БД) */ public function getAllModules(): array { $modules = $this->modulesConfig; $builder = $this->db->table($this->moduleSettingsTable); $settings = $builder->get()->getResultArray(); foreach ($settings as $setting) { $code = $setting['module_code']; if (isset($modules[$code])) { if (!empty($setting['name'])) { $modules[$code]['name'] = $setting['name']; } if (isset($setting['description']) && $setting['description'] !== '') { $modules[$code]['description'] = $setting['description']; } if (isset($setting['price_monthly'])) { $modules[$code]['price_monthly'] = (int) $setting['price_monthly']; } if (isset($setting['price_yearly'])) { $modules[$code]['price_yearly'] = (int) $setting['price_yearly']; } if (isset($setting['trial_days'])) { $modules[$code]['trial_days'] = (int) $setting['trial_days']; } } } return $modules; } /** * Проверка активности модуля для организации */ public function isModuleActive(string $moduleCode, ?int $organizationId = null): bool { if ($moduleCode === 'base') { return true; } $orgId = $organizationId ?? session()->get('active_org_id'); if (!$orgId) { return false; } $subscription = $this->getSubscription($orgId, $moduleCode); if (!$subscription) { return false; } return $subscription['status'] === 'active'; } /** * Проверка доступности модуля (активен или в триале) */ public function isModuleAvailable(string $moduleCode, ?int $organizationId = null): bool { if ($moduleCode === 'base') { return true; } $orgId = $organizationId ?? session()->get('active_org_id'); if (!$orgId) { return false; } $subscription = $this->getSubscription($orgId, $moduleCode); if (!$subscription) { return false; } return in_array($subscription['status'], ['active', 'trial'], true); } /** * Получение подписки организации на модуль */ public function getSubscription(int $organizationId, string $moduleCode): ?array { $builder = $this->db->table($this->subscriptionsTable); return $builder->where('organization_id', $organizationId) ->where('module_code', $moduleCode) ->get() ->getRowArray(); } /** * Получение всех подписок организации */ public function getOrganizationSubscriptions(int $organizationId): array { $builder = $this->db->table($this->subscriptionsTable); return $builder->where('organization_id', $organizationId) ->orderBy('created_at', 'DESC') ->get() ->getResultArray(); } /** * Получение всех активных модулей организации */ public function getActiveModules(int $organizationId): array { $builder = $this->db->table($this->subscriptionsTable); $subscriptions = $builder->where('organization_id', $organizationId) ->whereIn('status', ['active', 'trial']) ->get() ->getResultArray(); $modules = array_column($subscriptions, 'module_code'); $modules[] = 'base'; return array_unique($modules); } /** * Создание/обновление подписки */ public function upsertSubscription( int $organizationId, string $moduleCode, string $status = 'active', ?int $days = null ): bool { $existing = $this->getSubscription($organizationId, $moduleCode); $data = [ 'organization_id' => $organizationId, 'module_code' => $moduleCode, 'status' => $status, 'expires_at' => $days > 0 ? date('Y-m-d H:i:s', strtotime("+{$days} days")) : null, 'updated_at' => date('Y-m-d H:i:s'), ]; if ($existing) { return $this->db->table($this->subscriptionsTable) ->where('id', $existing['id']) ->update($data); } $data['created_at'] = date('Y-m-d H:i:s'); return $this->db->table($this->subscriptionsTable)->insert($data); } /** * Удаление подписки */ public function deleteSubscription(int $subscriptionId): bool { return $this->db->table($this->subscriptionsTable) ->where('id', $subscriptionId) ->delete(); } /** * Запуск триала для модуля */ public function startTrial(int $organizationId, string $moduleCode, int $trialDays = 14): bool { $config = $this->getModuleConfig($moduleCode); if (!$config || $config['trial_days'] <= 0) { return false; } return $this->upsertSubscription( $organizationId, $moduleCode, 'trial', $trialDays ); } /** * Активация подписки */ public function activate(int $organizationId, string $moduleCode, int $months = 1): bool { return $this->upsertSubscription( $organizationId, $moduleCode, 'active', $months * 30 ); } /** * Отмена подписки */ public function cancel(int $organizationId, string $moduleCode): bool { $existing = $this->getSubscription($organizationId, $moduleCode); if (!$existing) { return false; } return $this->db->table($this->subscriptionsTable) ->where('id', $existing['id']) ->update(['status' => 'cancelled', 'updated_at' => date('Y-m-d H:i:s')]); } /** * Получение всех подписок (для суперадмина) */ public function getAllSubscriptions(): array { $builder = $this->db->table($this->subscriptionsTable); return $builder ->select('organization_subscriptions.*, organizations.name as organization_name') ->join('organizations', 'organizations.id = organization_subscriptions.organization_id') ->orderBy('organization_subscriptions.created_at', 'DESC') ->get() ->getResultArray(); } /** * Статистика по модулям (для суперадмина) */ public function getModuleStats(): array { $stats = []; $modules = $this->getAllModules(); foreach ($modules as $code => $module) { $activeCount = $this->db->table($this->subscriptionsTable) ->where('module_code', $code) ->where('status', 'active') ->countAllResults(); $trialCount = $this->db->table($this->subscriptionsTable) ->where('module_code', $code) ->where('status', 'trial') ->countAllResults(); $stats[$code] = [ 'name' => $module['name'], 'active' => $activeCount, 'trial' => $trialCount, ]; } return $stats; } /** * Сохранение настроек модуля */ public function saveModuleSettings( string $moduleCode, ?string $name = null, ?string $description = null, ?int $priceMonthly = null, ?int $priceYearly = null, ?int $trialDays = null ): bool { $existing = $this->db->table($this->moduleSettingsTable) ->where('module_code', $moduleCode) ->get() ->getRowArray(); $data = [ 'module_code' => $moduleCode, 'updated_at' => date('Y-m-d H:i:s'), ]; if ($name !== null) { $data['name'] = $name; } if ($description !== null) { $data['description'] = $description; } if ($priceMonthly !== null) { $data['price_monthly'] = $priceMonthly; } if ($priceYearly !== null) { $data['price_yearly'] = $priceYearly; } if ($trialDays !== null) { $data['trial_days'] = $trialDays; } if ($existing) { return $this->db->table($this->moduleSettingsTable) ->where('id', $existing['id']) ->update($data); } $data['created_at'] = date('Y-m-d H:i:s'); $data['is_active'] = 1; return $this->db->table($this->moduleSettingsTable)->insert($data); } /** * Получение настроек модуля */ public function getModuleSettings(string $moduleCode): ?array { return $this->db->table($this->moduleSettingsTable) ->where('module_code', $moduleCode) ->get() ->getRowArray(); } }