Files
zemailnator/app/Models/TrialConfiguration.php
idevakk b497f7796d feat: implement comprehensive enhanced plan management system
- Create 7 new models with full relationships and business logic:
     * PlanFeature: Define available features with categories and types
     * PlanFeatureLimit: Manage usage limits per plan with trial overrides
     * PlanPermission: Granular permissions system for features
     * PlanProvider: Multi-provider payment configuration
     * PlanTier: Hierarchical plan structure with upgrade paths
     * PlanUsage: Real-time usage tracking and analytics
     * TrialConfiguration: Advanced trial settings per plan

   - Enhance Plan model with 25+ new methods:
     * Feature checking: hasFeature(), canUseFeature(), getRemainingUsage()
     * Permission system: hasPermission() with trial support
     * Payment providers: getAllowedProviders(), supportsProvider()
     * Trial management: hasTrial(), getTrialConfig()
     * Upgrade paths: isUpgradeFrom(), getUpgradePath()
     * Utility methods: getBillingCycleDisplay(), metadata handling

   - Completely redesign PlanResource with tabbed interface:
     * Basic Info: Core plan configuration with dynamic billing cycles
     * Features & Limits: Dynamic feature management with trial overrides
     * Payment Providers: Multi-provider configuration (Stripe, Lemon Squeezy, etc.)
     * Trial Settings: Advanced trial configuration with always-visible toggle

   - Create new Filament resources:
     * PlanFeatureResource: Manage available features by category
     * PlanTierResource: Hierarchical tier management with parent-child relationships

   - Implement comprehensive data migration:
     * Migrate legacy plan data to new enhanced system
     * Create default features (mailbox accounts, email forwarding, etc.)
     * Preserve existing payment provider configurations
     * Set up trial configurations (disabled for legacy plans)
     * Handle duplicate data gracefully with rollback support

   - Add proper database constraints and indexes:
     * Unique constraints on plan-feature relationships
     * Foreign key constraints with cascade deletes
     * Performance indexes for common queries
     * JSON metadata columns for flexible configuration

   - Fix trial configuration form handling:
     * Add required validation for numeric fields
     * Implement proper null handling with defaults
     * Add type casting for all numeric fields
     * Ensure database constraint compliance
2025-11-21 10:13:14 -08:00

72 lines
1.8 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TrialConfiguration extends Model
{
use HasFactory;
protected $fillable = [
'plan_id',
'trial_enabled',
'trial_duration_days',
'trial_requires_payment_method',
'trial_auto_converts',
'trial_conversion_action',
'trial_extension_limit',
'trial_feature_overrides',
'trial_welcome_message',
'trial_expiry_message',
];
protected $casts = [
'trial_enabled' => 'boolean',
'trial_duration_days' => 'integer',
'trial_requires_payment_method' => 'boolean',
'trial_auto_converts' => 'boolean',
'trial_conversion_action' => 'string',
'trial_extension_limit' => 'integer',
'trial_feature_overrides' => 'array',
];
public const ACTION_UPGRADE_TO_PAID = 'upgrade_to_paid';
public const ACTION_CANCEL = 'cancel';
public const ACTION_NOTIFY = 'notify';
public function plan(): BelongsTo
{
return $this->belongsTo(Plan::class);
}
public function scopeEnabled($query)
{
return $query->where('trial_enabled', true);
}
public function canExtendTrial(int $currentExtensions = 0): bool
{
return $this->trial_extension_limit > $currentExtensions;
}
public function getTrialEndDate(): \Carbon\Carbon
{
return now()->addDays($this->trial_duration_days);
}
public function hasFeatureOverride(string $featureName): bool
{
return isset($this->trial_feature_overrides[$featureName]);
}
public function getFeatureOverride(string $featureName)
{
return $this->trial_feature_overrides[$featureName] ?? null;
}
}