Fix: Corriger le type PDO dans StripeService et retirer getConnection()
This commit is contained in:
213
app/lib/core/services/stripe_connect_service.dart
Normal file
213
app/lib/core/services/stripe_connect_service.dart
Normal file
@@ -0,0 +1,213 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
|
||||
/// Service pour gérer Stripe Connect dans l'application
|
||||
class StripeConnectService {
|
||||
final ApiService apiService;
|
||||
|
||||
StripeConnectService({required this.apiService});
|
||||
|
||||
/// Créer un compte Stripe Connect pour une amicale
|
||||
/// Retourne l'URL d'onboarding ou null si erreur
|
||||
Future<String?> createStripeAccount(AmicaleModel amicale) async {
|
||||
try {
|
||||
debugPrint('🏢 Création compte Stripe pour ${amicale.name}...');
|
||||
debugPrint(' Amicale ID: ${amicale.id}');
|
||||
|
||||
// 1. Créer le compte Stripe Connect via notre API
|
||||
final createResponse = await apiService.post(
|
||||
'/stripe/accounts',
|
||||
data: {'fk_entite': amicale.id},
|
||||
);
|
||||
|
||||
debugPrint(' Response status: ${createResponse.statusCode}');
|
||||
debugPrint(' Response data: ${createResponse.data}');
|
||||
|
||||
if (createResponse.statusCode != 200 && createResponse.statusCode != 201) {
|
||||
final error = createResponse.data?['message'] ?? 'Erreur création compte';
|
||||
|
||||
// Si le compte existe déjà, récupérer l'account_id
|
||||
if (error.toString().contains('existe déjà')) {
|
||||
final accountId = createResponse.data?['account_id'];
|
||||
debugPrint(' Compte existant détecté, account_id: $accountId');
|
||||
if (accountId != null) {
|
||||
return await getOnboardingLink(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('❌ Erreur création compte: $error');
|
||||
throw Exception(error);
|
||||
}
|
||||
|
||||
final accountId = createResponse.data?['account_id'];
|
||||
debugPrint('✅ Compte créé/récupéré: $accountId');
|
||||
|
||||
if (accountId == null) {
|
||||
throw Exception('account_id non retourné par l\'API');
|
||||
}
|
||||
|
||||
// 2. Créer la Location pour Terminal/Tap to Pay
|
||||
try {
|
||||
await apiService.post(
|
||||
'/stripe/locations',
|
||||
data: {'fk_entite': amicale.id},
|
||||
);
|
||||
debugPrint('✅ Location Terminal créée');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur création Location (non bloquant): $e');
|
||||
}
|
||||
|
||||
// 3. Obtenir le lien d'onboarding
|
||||
return await getOnboardingLink(accountId);
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur StripeConnect: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtenir le lien d'onboarding pour finaliser la configuration
|
||||
Future<String?> getOnboardingLink(String accountId) async {
|
||||
try {
|
||||
debugPrint('📋 Génération du lien d\'onboarding pour account: $accountId');
|
||||
|
||||
// URLs de retour après onboarding
|
||||
const baseUrl = 'https://dapp.geosector.fr'; // À adapter selon l'environnement
|
||||
final returnUrl = Uri.encodeFull('$baseUrl/stripe/success');
|
||||
final refreshUrl = Uri.encodeFull('$baseUrl/stripe/refresh');
|
||||
|
||||
debugPrint(' Return URL: $returnUrl');
|
||||
debugPrint(' Refresh URL: $refreshUrl');
|
||||
|
||||
final response = await apiService.post(
|
||||
'/stripe/accounts/$accountId/onboarding-link',
|
||||
data: {
|
||||
'return_url': returnUrl,
|
||||
'refresh_url': refreshUrl,
|
||||
},
|
||||
);
|
||||
|
||||
debugPrint(' Response status: ${response.statusCode}');
|
||||
debugPrint(' Response data: ${response.data}');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final url = response.data['url'];
|
||||
debugPrint('✅ Lien onboarding généré: $url');
|
||||
return url;
|
||||
}
|
||||
|
||||
final errorMessage = response.data?['message'] ?? 'Erreur inconnue';
|
||||
throw Exception('Impossible de générer le lien: $errorMessage');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur onboarding link: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifier le statut du compte Stripe d'une amicale
|
||||
Future<StripeAccountStatus> checkAccountStatus(int amicaleId) async {
|
||||
try {
|
||||
final response = await apiService.get('/stripe/accounts/$amicaleId/status');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = response.data;
|
||||
return StripeAccountStatus(
|
||||
hasAccount: data['has_account'] ?? false,
|
||||
accountId: data['account_id'],
|
||||
chargesEnabled: data['charges_enabled'] ?? false,
|
||||
payoutsEnabled: data['payouts_enabled'] ?? false,
|
||||
onboardingCompleted: data['onboarding_completed'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
return StripeAccountStatus(hasAccount: false);
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur vérification statut: $e');
|
||||
return StripeAccountStatus(hasAccount: false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ouvrir le Dashboard Stripe pour l'amicale
|
||||
Future<void> openStripeDashboard(String? accountId) async {
|
||||
if (accountId == null || accountId.isEmpty) return;
|
||||
|
||||
// URL du dashboard Stripe Express
|
||||
final url = Uri.parse('https://dashboard.stripe.com/express/$accountId');
|
||||
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lancer le processus d'onboarding dans un navigateur externe
|
||||
Future<bool> launchOnboarding(String url) async {
|
||||
try {
|
||||
debugPrint('🚀 Lancement onboarding URL: $url');
|
||||
debugPrint(' Plateforme: ${kIsWeb ? "Web" : "Mobile"}');
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
|
||||
// Sur web, on peut directement lancer sans vérification
|
||||
if (kIsWeb) {
|
||||
final launched = await launchUrl(
|
||||
uri,
|
||||
webOnlyWindowName: '_blank', // Ouvre dans un nouvel onglet
|
||||
);
|
||||
debugPrint(launched ? '✅ URL lancée avec succès' : '❌ Échec du lancement');
|
||||
return launched;
|
||||
} else {
|
||||
// Sur mobile, vérifier d'abord si on peut lancer l'URL
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(
|
||||
uri,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
debugPrint('✅ URL lancée avec succès');
|
||||
return true;
|
||||
} else {
|
||||
debugPrint('❌ Impossible de lancer l\'URL sur mobile');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lancement onboarding: $e');
|
||||
debugPrint(' Détails: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Statut d'un compte Stripe Connect
|
||||
class StripeAccountStatus {
|
||||
final bool hasAccount;
|
||||
final String? accountId;
|
||||
final bool chargesEnabled;
|
||||
final bool payoutsEnabled;
|
||||
final bool onboardingCompleted;
|
||||
|
||||
StripeAccountStatus({
|
||||
required this.hasAccount,
|
||||
this.accountId,
|
||||
this.chargesEnabled = false,
|
||||
this.payoutsEnabled = false,
|
||||
this.onboardingCompleted = false,
|
||||
});
|
||||
|
||||
bool get canAcceptPayments => chargesEnabled && payoutsEnabled;
|
||||
|
||||
String get statusMessage {
|
||||
if (!hasAccount) return 'Pas de compte Stripe';
|
||||
if (!onboardingCompleted) return 'Configuration en cours';
|
||||
if (!chargesEnabled) return 'Paiements non activés';
|
||||
if (!payoutsEnabled) return 'Virements non activés';
|
||||
return 'Compte actif';
|
||||
}
|
||||
|
||||
Color get statusColor {
|
||||
if (canAcceptPayments) return Colors.green;
|
||||
if (hasAccount) return Colors.orange;
|
||||
return Colors.red;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
import 'package:geosector_app/core/repositories/user_repository.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/services/stripe_connect_service.dart';
|
||||
import 'package:geosector_app/presentation/widgets/mapbox_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
@@ -66,6 +67,11 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
XFile? _selectedImage;
|
||||
String? _logoUrl;
|
||||
|
||||
// Pour Stripe Connect
|
||||
StripeConnectService? _stripeService;
|
||||
bool _isCheckingStripeStatus = false;
|
||||
StripeAccountStatus? _stripeStatus;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -97,6 +103,159 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
_chkUserDeletePass = amicale?.chkUserDeletePass ?? false;
|
||||
|
||||
// Note : Le logo sera chargé dynamiquement depuis l'API
|
||||
|
||||
// Initialiser le service Stripe si API disponible
|
||||
if (widget.apiService != null) {
|
||||
_stripeService = StripeConnectService(apiService: widget.apiService!);
|
||||
// Vérifier le statut Stripe si l'amicale a déjà un compte
|
||||
if (_chkStripe && widget.amicale != null) {
|
||||
_checkStripeStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier le statut du compte Stripe
|
||||
Future<void> _checkStripeStatus() async {
|
||||
if (_stripeService == null || widget.amicale == null) return;
|
||||
|
||||
setState(() => _isCheckingStripeStatus = true);
|
||||
|
||||
try {
|
||||
final status = await _stripeService!.checkAccountStatus(widget.amicale!.id);
|
||||
setState(() {
|
||||
_stripeStatus = status;
|
||||
if (status.accountId != null) {
|
||||
_stripeIdController.text = status.accountId!;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint('Erreur vérification statut Stripe: $e');
|
||||
} finally {
|
||||
setState(() => _isCheckingStripeStatus = false);
|
||||
}
|
||||
}
|
||||
|
||||
// Configurer Stripe Connect
|
||||
Future<void> _configureStripe() async {
|
||||
if (_stripeService == null || widget.amicale == null) return;
|
||||
|
||||
// Vérifier que nous sommes sur Web
|
||||
if (!kIsWeb) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Configuration Web requise'),
|
||||
content: const Text(
|
||||
'La configuration du compte Stripe doit être effectuée depuis un navigateur web.\n\n'
|
||||
'Veuillez vous connecter depuis un ordinateur pour configurer les paiements par carte bancaire.',
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Compris'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Afficher un dialog de confirmation
|
||||
final bool? confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Configuration Stripe'),
|
||||
content: const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Vous allez être redirigé vers Stripe pour :'),
|
||||
SizedBox(height: 8),
|
||||
Text('• Créer votre compte marchand'),
|
||||
Text('• Configurer vos informations bancaires'),
|
||||
Text('• Activer les paiements par carte'),
|
||||
SizedBox(height: 16),
|
||||
Text('Ce processus prend environ 5-10 minutes.'),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Continuer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm != true) return;
|
||||
|
||||
// Afficher le loading
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => const AlertDialog(
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 16),
|
||||
Text('Préparation de votre compte Stripe...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
// Créer ou récupérer le lien d'onboarding
|
||||
final url = await _stripeService!.createStripeAccount(widget.amicale!);
|
||||
|
||||
// Fermer le loading
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
|
||||
if (url != null) {
|
||||
// Lancer l'onboarding
|
||||
final success = await _stripeService!.launchOnboarding(url);
|
||||
|
||||
if (success) {
|
||||
// Activer la checkbox
|
||||
setState(() => _chkStripe = true);
|
||||
|
||||
// Afficher un message de succès
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Configuration Stripe lancée. Revenez ici après avoir terminé.'),
|
||||
duration: Duration(seconds: 5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier le statut après un délai
|
||||
Future.delayed(const Duration(seconds: 5), _checkStripeStatus);
|
||||
}
|
||||
} else {
|
||||
throw Exception('Impossible de créer le lien de configuration');
|
||||
}
|
||||
} catch (e) {
|
||||
// Fermer le loading si encore ouvert
|
||||
if (mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
// Afficher l'erreur
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur: ${e.toString()}'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -744,7 +903,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
}
|
||||
|
||||
// Construire le formulaire principal
|
||||
Widget _buildMainForm(ThemeData theme, bool restrictedFieldsReadOnly) {
|
||||
Widget _buildMainForm(ThemeData theme, bool restrictedFieldsReadOnly, bool stripeReadOnly) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -976,78 +1135,133 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Stripe Checkbox et Stripe ID sur la même ligne
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// Stripe Checkbox et configuration
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Checkbox Stripe
|
||||
Checkbox(
|
||||
value: _chkStripe,
|
||||
onChanged: restrictedFieldsReadOnly
|
||||
? null
|
||||
: (value) {
|
||||
if (value == true) {
|
||||
// Afficher une boîte de dialogue de confirmation
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Confirmation'),
|
||||
content: const Text(
|
||||
'En acceptant les règlements par carte bancaire, des commissions de 1.4% seront prélevées sur les montants encaissés. Souhaitez-vous continuer ?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// L'utilisateur a répondu "non"
|
||||
setState(() {
|
||||
_chkStripe = false;
|
||||
});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Non'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// L'utilisateur a répondu "oui"
|
||||
setState(() {
|
||||
_chkStripe = true;
|
||||
});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF20335E),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text('Oui'),
|
||||
),
|
||||
],
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Checkbox Stripe
|
||||
Checkbox(
|
||||
value: _chkStripe,
|
||||
onChanged: stripeReadOnly
|
||||
? null
|
||||
: (value) {
|
||||
setState(() {
|
||||
_chkStripe = value ?? false;
|
||||
});
|
||||
// Si on active la checkbox et qu'on a une amicale, vérifier le statut
|
||||
if (value == true && widget.amicale != null) {
|
||||
_checkStripeStatus();
|
||||
}
|
||||
},
|
||||
activeColor: const Color(0xFF20335E),
|
||||
),
|
||||
Text(
|
||||
"Accepte les règlements en CB",
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Bouton Configuration Stripe si checkbox cochée
|
||||
if (_chkStripe && !stripeReadOnly && widget.amicale != null) ...[
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isCheckingStripeStatus ? null : _configureStripe,
|
||||
icon: _isCheckingStripeStatus
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
)
|
||||
: Icon(kIsWeb ? Icons.settings : Icons.computer, size: 18),
|
||||
label: Text(
|
||||
!kIsWeb
|
||||
? 'Config. Web requise'
|
||||
: (_stripeStatus?.canAcceptPayments == true
|
||||
? 'Compte actif'
|
||||
: 'Configurer Stripe')
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: !kIsWeb
|
||||
? Colors.grey
|
||||
: (_stripeStatus?.statusColor ?? Colors.orange),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Indicateur de statut
|
||||
if (_stripeStatus != null)
|
||||
Tooltip(
|
||||
message: _stripeStatus!.statusMessage,
|
||||
child: Icon(
|
||||
_stripeStatus!.canAcceptPayments
|
||||
? Icons.check_circle
|
||||
: Icons.warning_amber_rounded,
|
||||
color: _stripeStatus!.statusColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
// Stripe ID (sur une ligne séparée)
|
||||
const SizedBox(height: 8),
|
||||
CustomTextField(
|
||||
controller: _stripeIdController,
|
||||
label: "ID Compte Stripe",
|
||||
readOnly: true,
|
||||
helperText: _chkStripe
|
||||
? (_stripeStatus?.accountId != null
|
||||
? "Compte Stripe Connect: ${_stripeStatus!.accountId}"
|
||||
: "L'ID sera généré automatiquement lors de la configuration")
|
||||
: "Activez les paiements CB pour configurer Stripe",
|
||||
),
|
||||
// Message d'information sur les commissions
|
||||
if (_chkStripe)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: _stripeStatus?.statusColor.withOpacity(0.1) ?? Colors.orange.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: _stripeStatus?.statusColor.withOpacity(0.3) ?? Colors.orange.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
_stripeStatus?.canAcceptPayments == true
|
||||
? Icons.check_circle_outline
|
||||
: Icons.info_outline,
|
||||
color: _stripeStatus?.statusColor ?? Colors.orange,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_stripeStatus?.canAcceptPayments == true
|
||||
? "✅ Compte Stripe configuré - Commission plateforme: 2.5% (min 0.50€)"
|
||||
: _stripeStatus?.onboardingCompleted == false
|
||||
? "⏳ Configuration Stripe en cours. Veuillez compléter le processus d'onboarding."
|
||||
: "💳 Les paiements CB seront soumis à une commission de 2.5% (min 0.50€)",
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Si l'utilisateur décoche la case, pas besoin de confirmation
|
||||
setState(() {
|
||||
_chkStripe = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
activeColor: const Color(0xFF20335E),
|
||||
),
|
||||
Text(
|
||||
"Accepte les règlements en CB",
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Stripe ID
|
||||
Expanded(
|
||||
child: CustomTextField(
|
||||
controller: _stripeIdController,
|
||||
label: "ID Stripe Paiements CB",
|
||||
readOnly: restrictedFieldsReadOnly,
|
||||
helperText: "Les règlements par CB sont taxés d'une commission de 1.4%",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -1251,9 +1465,15 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
|
||||
// Déterminer si l'utilisateur peut modifier les champs restreints
|
||||
final bool canEditRestrictedFields = userRole > 2;
|
||||
|
||||
// Pour Stripe, les admins d'amicale (rôle 2) peuvent aussi configurer
|
||||
final bool canEditStripe = userRole >= 2;
|
||||
|
||||
// Lecture seule pour les champs restreints si l'utilisateur n'a pas les droits
|
||||
final bool restrictedFieldsReadOnly = widget.readOnly || !canEditRestrictedFields;
|
||||
|
||||
// Lecture seule spécifique pour Stripe
|
||||
final bool stripeReadOnly = widget.readOnly || !canEditStripe;
|
||||
|
||||
// Calculer la largeur maximale du formulaire pour les écrans larges
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
@@ -1281,7 +1501,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Formulaire principal
|
||||
_buildMainForm(theme, restrictedFieldsReadOnly),
|
||||
_buildMainForm(theme, restrictedFieldsReadOnly, stripeReadOnly),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user