import 'package:equatable/equatable.dart'; // =================================================================== // ENUMS (Paranoia Mode per la lettura dal DB) // =================================================================== enum SubscriptionTier { free, pro, premium, platinum; static SubscriptionTier fromString(String? value) { return SubscriptionTier.values.firstWhere( (e) => e.name == value, orElse: () => SubscriptionTier.free, // Fallback di sicurezza ); } } enum SubscriptionStatus { trialing, active, pastDue, canceled; static SubscriptionStatus fromString(String? value) { if (value == null) return SubscriptionStatus.canceled; // Normalizziamo 'past_due' dal DB in 'pastdue' per il match con l'Enum final normalizedValue = value.replaceAll('_', '').toLowerCase(); return SubscriptionStatus.values.firstWhere( (e) => e.name.toLowerCase() == normalizedValue, orElse: () => SubscriptionStatus.canceled, ); } } // =================================================================== // IL MODELLO ESATTO // =================================================================== class CompanyModel extends Equatable { final String? id; final DateTime? createdAt; final String userId; // Nel DB è user_id (chiave esterna su auth.users) // Dati Anagrafici e Fatturazione final String name; final String address; final String zipCode; final String city; final String province; final String vatId; final String fiscalCode; final String sdi; final String? phone; final String? email; final String? logoUrl; // Stato Pagamenti (Ibride: manuale + Stripe) final bool isPaid; final DateTime? paymentExpiration; // Campi SaaS Stripe/Automazioni final SubscriptionTier subscriptionTier; final SubscriptionStatus subscriptionStatus; final DateTime? trialEndsAt; final String? stripeCustomerId; final String? stripeSubscriptionId; const CompanyModel({ this.id, this.createdAt, required this.userId, required this.name, required this.address, required this.zipCode, required this.city, required this.province, required this.vatId, required this.fiscalCode, required this.sdi, this.phone, this.email, this.logoUrl, this.isPaid = false, this.paymentExpiration, this.subscriptionTier = SubscriptionTier.free, this.subscriptionStatus = SubscriptionStatus.trialing, this.trialEndsAt, this.stripeCustomerId, this.stripeSubscriptionId, }); CompanyModel copyWith({ String? id, DateTime? createdAt, String? userId, String? name, String? address, String? zipCode, String? city, String? province, String? vatId, String? fiscalCode, String? sdi, String? logoUrl, String? phone, String? email, bool? isPaid, DateTime? paymentExpiration, SubscriptionTier? subscriptionTier, SubscriptionStatus? subscriptionStatus, DateTime? trialEndsAt, String? stripeCustomerId, String? stripeSubscriptionId, }) { return CompanyModel( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, userId: userId ?? this.userId, name: name ?? this.name, address: address ?? this.address, zipCode: zipCode ?? this.zipCode, city: city ?? this.city, province: province ?? this.province, vatId: vatId ?? this.vatId, fiscalCode: fiscalCode ?? this.fiscalCode, sdi: sdi ?? this.sdi, logoUrl: logoUrl ?? this.logoUrl, phone: phone ?? this.phone, email: email ?? this.email, isPaid: isPaid ?? this.isPaid, paymentExpiration: paymentExpiration ?? this.paymentExpiration, subscriptionTier: subscriptionTier ?? this.subscriptionTier, subscriptionStatus: subscriptionStatus ?? this.subscriptionStatus, trialEndsAt: trialEndsAt ?? this.trialEndsAt, stripeCustomerId: stripeCustomerId ?? this.stripeCustomerId, stripeSubscriptionId: stripeSubscriptionId ?? this.stripeSubscriptionId, ); } factory CompanyModel.empty() { return const CompanyModel( id: null, createdAt: null, userId: '', name: '', address: '', zipCode: '', city: '', province: '', vatId: '', fiscalCode: '', sdi: '', ); } factory CompanyModel.fromMap(Map map) { return CompanyModel( id: map['id'] as String?, createdAt: map['created_at'] != null ? DateTime.tryParse(map['created_at']) : null, userId: map['user_id'] ?? '', name: map['name'] ?? '', address: map['address'] ?? '', zipCode: map['zip_code'] ?? '', city: map['city'] ?? '', province: map['province'] ?? '', vatId: map['vat_id'] ?? '', fiscalCode: map['fiscal_code'] ?? '', sdi: map['sdi'] ?? '', logoUrl: map['company_logo'], phone: map['phone'] ?? '', email: map['email'] ?? '', isPaid: map['is_paid'] ?? false, paymentExpiration: map['payment_expiration'] != null ? DateTime.tryParse(map['payment_expiration']) : null, subscriptionTier: SubscriptionTier.fromString(map['subscription_tier']), subscriptionStatus: SubscriptionStatus.fromString( map['subscription_status'], ), trialEndsAt: map['trial_ends_at'] != null ? DateTime.tryParse(map['trial_ends_at']) : null, stripeCustomerId: map['stripe_customer_id'], stripeSubscriptionId: map['stripe_subscription_id'], ); } Map toMap() { return { if (id != null) 'id': id, // created_at è gestito dal DB di default, di solito non si passa nell'insert 'user_id': userId, 'name': name, 'address': address, 'zip_code': zipCode, 'city': city, 'province': province, 'vat_id': vatId, 'fiscal_code': fiscalCode, 'sdi': sdi, 'company_logo': logoUrl, 'phone': phone, 'email': 'email', 'is_paid': isPaid, if (paymentExpiration != null) 'payment_expiration': paymentExpiration!.toIso8601String(), 'subscription_tier': subscriptionTier.name, 'subscription_status': subscriptionStatus == SubscriptionStatus.pastDue ? 'past_due' : subscriptionStatus.name, if (trialEndsAt != null) 'trial_ends_at': trialEndsAt!.toIso8601String(), if (stripeCustomerId != null) 'stripe_customer_id': stripeCustomerId, if (stripeSubscriptionId != null) 'stripe_subscription_id': stripeSubscriptionId, }; } @override List get props => [ id, createdAt, userId, name, address, zipCode, city, province, vatId, fiscalCode, sdi, logoUrl, phone, email, isPaid, paymentExpiration, subscriptionTier, subscriptionStatus, trialEndsAt, stripeCustomerId, stripeSubscriptionId, ]; } // =================================================================== // BUSINESS LOGIC: I LIMITI DEI TIER // =================================================================== extension CompanyLimits on CompanyModel { int get maxStores { switch (subscriptionTier) { case SubscriptionTier.free: return 1; case SubscriptionTier.pro: return 1; case SubscriptionTier.premium: return 10; case SubscriptionTier.platinum: return 30; } } int get maxStaffMembers { switch (subscriptionTier) { case SubscriptionTier.free: return 2; case SubscriptionTier.pro: return 10; case SubscriptionTier.premium: return 50; case SubscriptionTier.platinum: return 150; } } int get maxOperationsPerMonth { switch (subscriptionTier) { case SubscriptionTier.free: return 50; case SubscriptionTier.pro: return 1000; case SubscriptionTier.premium: return 10000; case SubscriptionTier.platinum: return 30000; } } int get maxStorageGb { switch (subscriptionTier) { case SubscriptionTier.free: return 0; // 500MB = 0.5 GB, magari qui usi i MB interi case SubscriptionTier.pro: return 3; case SubscriptionTier.premium: return 30; case SubscriptionTier.platinum: return 100; } } /// Verifica generale: L'utente ha i permessi per usare l'app? bool get hasActiveAccess { // 1. Priorità all'override manuale (is_paid e payment_expiration) if (isPaid) { if (paymentExpiration == null) { return true; // Pagato "a vita" o senza scadenza } if (DateTime.now().isBefore(paymentExpiration!)) return true; } // 2. Controllo SaaS (Stripe/Subscription) if (subscriptionStatus == SubscriptionStatus.active) return true; // 3. Controllo Trial if (subscriptionStatus == SubscriptionStatus.trialing && trialEndsAt != null) { return DateTime.now().isBefore(trialEndsAt!); } // Scaduto, past_due, o cancellato return false; } }