import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/features/customers/models/customer_model.dart'; import 'package:flux/features/master_data/providers/models/provider_model.dart'; import 'package:flux/features/master_data/providers/models/provider_model_extensions.dart'; import 'package:flux/features/master_data/staff/models/staff_member_model.dart'; import 'package:flux/features/operations/data/operations_repository.dart'; import 'package:flux/features/operations/models/operation_model.dart'; import 'package:get_it/get_it.dart'; import 'package:uuid/uuid.dart'; part 'operation_form_state.dart'; class OperationFormCubit extends Cubit { final OperationsRepository _repository = GetIt.I(); final SessionCubit _sessionCubit = GetIt.I(); final Uuid _uuid = const Uuid(); OperationFormCubit({ StaffMemberModel? createdBy, OperationModel? existingOperation, }) : super( OperationFormState( operation: existingOperation ?? OperationModel.empty().copyWith( staffId: createdBy?.id, staffDisplayName: createdBy?.name, ), ), ); Future initForm({ OperationModel? existingOperation, String? operationId, }) async { emit(state.copyWith(status: OperationFormStatus.loading)); try { if (existingOperation != null) { emit( state.copyWith( operation: existingOperation, status: OperationFormStatus.ready, ), ); } else if (operationId != null) { emit(state.copyWith(status: OperationFormStatus.loading)); try { final operation = await _repository.fetchOperationById(operationId); emit( state.copyWith( operation: operation, status: OperationFormStatus.ready, ), ); } on Exception catch (e) { emit( state.copyWith( status: OperationFormStatus.failure, errorMessage: e.toString(), ), ); } } else { // NUOVA PRATICA: Creiamo un nuovo Batch UUID final currentStore = _sessionCubit.state.currentStore; final companyId = _sessionCubit.state.company?.id ?? ''; final newOperation = state.operation.copyWith( companyId: companyId, storeId: currentStore?.id, status: OperationStatus.success, reference: '', batchUuid: _uuid.v4(), ); emit( state.copyWith( operation: newOperation, status: OperationFormStatus.ready, ), ); } } catch (e) { emit( state.copyWith( status: OperationFormStatus.failure, errorMessage: "Errore inizializzazione form: $e", ), ); } } // --- LOGICA BATCH --- void _prepareNextOperationInBatch() { final current = state.operation; emit( state.copyWith( status: OperationFormStatus.ready, operation: OperationModel( companyId: current.companyId, storeId: current.storeId, storeDisplayName: current.storeDisplayName, // 🥷 REINSERIAMO LO STAFF (Il "colpevole" era qui) staffId: current.staffId, staffDisplayName: current.staffDisplayName, batchUuid: current.batchUuid, customerId: current.customerId, customer: current.customer, reference: current.reference, status: OperationStatus.draft, createdAt: DateTime.now(), // Mantieni isBusiness se vuoi che rimanga coerente col cliente isBusiness: current.isBusiness, ), ), ); } // --- SALVATAGGIO --- Future saveOperation({ required OperationStatus targetStatus, required bool keepAdding, }) async { emit( state.copyWith(status: OperationFormStatus.saving, errorMessage: null), ); try { OperationModel operationToSave = state.operation.copyWith( status: targetStatus, ); if (operationToSave.reference.isEmpty) { if (operationToSave.customer != null && operationToSave.customer!.phoneNumber.isNotEmpty) { operationToSave = operationToSave.copyWith( reference: '${operationToSave.customer?.phoneNumber} - auto', ); } else { operationToSave = operationToSave.copyWith( reference: 'Nessun riferimento', ); } } final savedOperation = await _repository.saveFullOperation( operation: operationToSave, ); if (keepAdding) { // Salviamo nella "memoria" del batch le pratiche create finora final updatedBatchList = List.from( state.savedBatchOperations, )..add(savedOperation); emit( state.copyWith( status: OperationFormStatus.successAndAddAnother, savedBatchOperations: updatedBatchList, ), ); // Pulisce i campi per la prossima operazione _prepareNextOperationInBatch(); } else { emit( state.copyWith( status: OperationFormStatus.success, operation: savedOperation, // Aggiorniamo con l'ID restituito dal DB ), ); } } catch (e) { emit( state.copyWith( status: OperationFormStatus.failure, errorMessage: e.toString(), ), ); } } Future saveOperationDraft() async { try { final operationToSave = state.operation; if (operationToSave.customerId == null || operationToSave.customerId!.isEmpty) { throw Exception('Seleziona un cliente prima di poter usare il QR'); } final savedOperation = await _repository.saveFullOperation( operation: operationToSave, ); emit( state.copyWith( operation: savedOperation, status: OperationFormStatus.ready, ), ); return savedOperation.id; } catch (e) { emit( state.copyWith( status: OperationFormStatus.failure, errorMessage: e.toString(), ), ); return null; } } // --- GESTIONE DEI CAMPI IN TEMPO REALE --- void updateFields({ String? reference, String? note, String? type, String? providerId, String? providerDisplayName, String? subType, String? description, DateTime? expirationDate, int? quantity, String? modelId, String? modelDisplayName, String? staffId, String? staffDisplayName, OperationStatus? status, bool? isBusiness, bool clearProvider = false, bool clearType = false, bool clearSubType = false, bool clearDescription = false, bool clearExpiration = false, bool clearQuantity = false, bool clearModel = false, }) { final current = state.operation; int? newQuantity; if (clearQuantity) newQuantity = 1; if (quantity != null && quantity <= 0) newQuantity = 0; if (quantity != null && quantity > 0) newQuantity = quantity; final updated = current.copyWith( reference: reference ?? current.reference, note: note ?? current.note, providerId: clearProvider ? null : (providerId ?? current.providerId), providerDisplayName: clearProvider ? null : (providerDisplayName ?? current.providerDisplayName), quantity: newQuantity ?? current.quantity, type: clearType ? null : (type ?? current.type), description: clearDescription ? null : (description ?? current.description), subType: clearSubType ? null : (subType ?? current.subType), expirationDate: clearExpiration ? null : (expirationDate ?? current.expirationDate), modelId: clearModel ? null : (modelId ?? current.modelId), modelDisplayName: clearModel ? null : (modelDisplayName ?? current.modelDisplayName), staffId: staffId ?? current.staffId, staffDisplayName: staffDisplayName ?? current.staffDisplayName, status: status ?? current.status, isBusiness: isBusiness ?? current.isBusiness, ); emit(state.copyWith(operation: updated)); } void updateCustomer(CustomerModel customer) { final bool isBusiness = customer.isBusiness; final updatedOperation = state.operation.copyWith( customer: customer, customerId: customer.id, isBusiness: isBusiness, ); emit(state.copyWith(operation: updatedOperation)); } // --- UTILS --- void updateOperationType( String newType, { required List allProviders, String? defaultProviderId, }) { // 1. Aggiorniamo il tipo nel modello in canna // (Presumo tu abbia un metodo copyWith o simile) final updatedOp = state.operation.copyWith(type: newType, subType: ''); // 2. Prepariamoci ad auto-selezionare il provider String? newProviderId = updatedOp.providerId; String? newProviderName = updatedOp.providerDisplayName; // 3. LA LOGICA DI DEFAULT if (defaultProviderId != null) { // Troviamo il provider di default nella lista final defaultProvider = allProviders .where((p) => p.id == defaultProviderId) .firstOrNull; if (defaultProvider != null) { // Usiamo l'extension appena creata! if (defaultProvider.supportsOperation(newType)) { newProviderId = defaultProvider.id; newProviderName = defaultProvider.name; } else { // Se cambi tipo (es. da Mobile a Luce) e il default non lo supporta, sbianchiamo newProviderId = null; newProviderName = null; } } } // Emettiamo il nuovo stato emit( state.copyWith( operation: updatedOp.copyWith( providerId: newProviderId, providerDisplayName: newProviderName, ), ), ); } void setTypeWithSmartDefaults({ required String newType, required List allProviders, String? defaultProviderId, }) { final currentOp = state.operation; // ----------------------------------------- // 1. SMART DATES: Calcolo Scadenze Default // ----------------------------------------- DateTime? defaultDate; final now = DateTime.now(); if (newType == 'Energy') { defaultDate = DateTime(now.year, now.month + 24, now.day); } if (newType == 'Fin') { defaultDate = DateTime(now.year, now.month + 30, now.day); } if (newType == 'Entertainment') { defaultDate = DateTime(now.year, now.month + 12, now.day); } // ----------------------------------------- // 2. SMART PROVIDER: Filtro e Auto-Selezione // ----------------------------------------- String? newProviderId = currentOp.providerId; String? newProviderName = currentOp.providerDisplayName; // A) Il provider attuale è ancora compatibile col nuovo tipo scelto? if (newProviderId != null && newProviderId.isNotEmpty) { final currentProvider = allProviders .where((p) => p.id == newProviderId) .firstOrNull; if (currentProvider == null || !currentProvider.supportsOperation(newType)) { // Non è più compatibile (es. da TIM fisso passo a Energy). Lo sbianchiamo! newProviderId = null; newProviderName = null; } } // B) Se non c'è un provider selezionato, proviamo ad auto-inserire quello di default del negozio if ((newProviderId == null || newProviderId.isEmpty) && defaultProviderId != null) { final defaultProvider = allProviders .where((p) => p.id == defaultProviderId) .firstOrNull; // Controlliamo che il default del negozio supporti questa specifica operazione if (defaultProvider != null && defaultProvider.supportsOperation(newType)) { newProviderId = defaultProvider.id; newProviderName = defaultProvider.name; } } // ----------------------------------------- // 3. EMISSIONE DELLO STATO PULITO // ----------------------------------------- emit( state.copyWith( operation: currentOp.copyWith( type: newType, subType: '', // Resettiamo il sottotipo per evitare incongruenze (es. passo da Luce a DAZN) expirationDate: defaultDate, // Impostiamo la scadenza di default se calcolata providerId: newProviderId, providerDisplayName: newProviderName, modelId: null, modelDisplayName: null, ), ), ); } }