This commit is contained in:
2026-05-04 19:32:14 +02:00
parent 94ad524bae
commit 1115d2cb87
14 changed files with 443 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import 'package:flux/core/theme/theme.dart';
import 'package:flux/features/customers/blocs/customers_cubit.dart';
import 'package:flux/features/customers/models/customer_model.dart';
import 'package:flux/features/customers/ui/customer_form.dart';
import 'package:flux/temp/migration_tools.dart';
import 'package:go_router/go_router.dart';
class CustomersContent extends StatefulWidget {
@@ -84,6 +85,12 @@ class _CustomersContentState extends State<CustomersContent> {
),
),
//TODO cancella quando import finito
ElevatedButton(
onPressed: () => migrateModelsToSupabase(),
child: const Text('migra clienti'),
),
// LISTA CLIENTI
Expanded(
child: BlocBuilder<CustomersCubit, CustomersState>(

88
lib/firebase_options.dart Normal file
View File

@@ -0,0 +1,88 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
return windows;
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyA8vQbyEt81DoAuRVDc_3W_VIKY-9F-XTw',
appId: '1:872447580790:web:10745e7f9afb447d5d9d57',
messagingSenderId: '872447580790',
projectId: 'assistenza-catelli',
authDomain: 'assistenza-catelli.firebaseapp.com',
storageBucket: 'assistenza-catelli.firebasestorage.app',
measurementId: 'G-HTSSNQJ15P',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyBSxpdLDlPnN0xjejlX_5JL19BDeSzKOr8',
appId: '1:872447580790:android:a1d8d57960451f935d9d57',
messagingSenderId: '872447580790',
projectId: 'assistenza-catelli',
storageBucket: 'assistenza-catelli.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyCkjOTW6BlckKIxQdp5TPnHuRfXFoVC3bY',
appId: '1:872447580790:ios:a87d56c718aa61e05d9d57',
messagingSenderId: '872447580790',
projectId: 'assistenza-catelli',
storageBucket: 'assistenza-catelli.firebasestorage.app',
iosBundleId: 'com.catellisrl.flux',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyCkjOTW6BlckKIxQdp5TPnHuRfXFoVC3bY',
appId: '1:872447580790:ios:a87d56c718aa61e05d9d57',
messagingSenderId: '872447580790',
projectId: 'assistenza-catelli',
storageBucket: 'assistenza-catelli.firebasestorage.app',
iosBundleId: 'com.catellisrl.flux',
);
static const FirebaseOptions windows = FirebaseOptions(
apiKey: 'AIzaSyA5uJhb8ksqKqdEWbMD5ra6JYXIGoaIdIM',
appId: '1:872447580790:web:3b1623eda6abdac75d9d57',
messagingSenderId: '872447580790',
projectId: 'assistenza-catelli',
authDomain: 'assistenza-catelli.firebaseapp.com',
storageBucket: 'assistenza-catelli.firebasestorage.app',
measurementId: 'G-J8LZTQ9NHB',
);
}

View File

@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -7,6 +8,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flux/features/attachments/data/attachments_repository.dart';
import 'package:flux/features/auth/bloc/auth_cubit.dart';
import 'package:flux/features/operations/data/operations_repository.dart';
import 'package:flux/firebase_options.dart';
import 'package:flux/l10n/app_localizations.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
@@ -102,6 +104,9 @@ Future<void> setupLocator() async {
getIt.registerSingleton<SessionCubit>(
SessionCubit(getIt<CoreRepository>(), getIt<SharedPreferences>()),
);
//TODO rimuovere dopo gli import
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
}
class FluxApp extends StatefulWidget {

View File

@@ -0,0 +1,148 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
Future<void> migrateCustomersToSupabase() async {
// 1. IL TUO COMPANY ID REALE SU SUPABASE
// Vai nel database Supabase, copia l'UUID della tua azienda e incollalo qui
final String myRealCompanyId = '6c4b2323-2a60-4d33-bf21-c5a8eb6b4a5b';
try {
print("Inizio download modello da Firebase...");
// 2. Scarichiamo TUTTI i clienti da Firebase
final snapshot = await FirebaseFirestore.instance.collection('marca').get();
if (snapshot.docs.isEmpty) {
print("Nessun marca trovato su Firebase!");
return;
}
// Questa lista conterrà i dati formattati pronti per Supabase
List<Map<String, dynamic>> supabaseBrands = [];
// 3. Cicliamo i documenti di Firebase e li trasformiamo
for (var doc in snapshot.docs) {
final data = doc.data();
// Creiamo la riga per Supabase
supabaseBrands.add({
'legacy_id': doc.id, // L'ID vecchio di Firebase
//'company_id': myRealCompanyId, // ECCO IL TUO COMPANY ID!
// Mappa i campi (attento a far combaciare i nomi esatti delle colonne Supabase!)
'name': (data['nome'] as String).trim().toLowerCase(),
'company_id': myRealCompanyId,
// Se avevi una data di creazione su Firebase, convertila, altrimenti ignorala
// e Supabase userà il suo 'default now()'
// 'created_at': (data['createdAt'] as Timestamp?)?.toDate().toIso8601String(),
});
}
print("Sto per inviare ${supabaseBrands.length} brand a Supabase...");
// 4. Invio a Supabase con UPSERT
await Supabase.instance.client
.from('brand')
.upsert(
supabaseBrands,
onConflict:
'legacy_id', // Se il legacy_id c'è già, aggiorna invece di duplicare
);
print("BOOM! Migrazione brand completata con successo! 🚀");
} catch (e) {
print("Porca miseria, errore durante la migrazione: $e");
}
}
Future<void> migrateModelsToSupabase() async {
final String myRealCompanyId = '6c4b2323-2a60-4d33-bf21-c5a8eb6b4a5b';
try {
print("Inizio migrazione Modelli...");
// ==========================================================
// FASE 1: CREAZIONE DEL DIZIONARIO DI TRADUZIONE (LA MAGIA)
// ==========================================================
print("Scarico i Brand da Supabase per tradurre gli ID...");
// Chiediamo a Supabase solo 2 colonne: il nuovo UUID e il vecchio ID di Firebase
final List<dynamic> brandResponse = await Supabase.instance.client
.from('brand')
.select('id, legacy_id');
// Creiamo la mappa: la chiave è il vecchio ID, il valore è il nuovo UUID
Map<String, String> brandTranslationMap = {};
for (var b in brandResponse) {
if (b['legacy_firestore_id'] != null) {
brandTranslationMap[b['legacy_id']] = b['id'];
}
}
print("Dizionario pronto! Trovati ${brandTranslationMap.length} Brand.");
// ==========================================================
// FASE 2: SCARICAMENTO E TRADUZIONE DEI MODELLI
// ==========================================================
final snapshot = await FirebaseFirestore.instance
.collection('modello')
.get(); // Controlla il nome esatto della collection!
if (snapshot.docs.isEmpty) {
print("Nessun modello trovato su Firebase!");
return;
}
List<Map<String, dynamic>> supabaseModels = [];
for (var doc in snapshot.docs) {
final data = doc.data();
// 1. Prendiamo il vecchio ID del brand salvato su Firebase
String? oldFirebaseBrandId = data['idMarca'];
// 2. TRADUZIONE ISTANTANEA! Cerchiamo il nuovo UUID nel nostro dizionario
String? newSupabaseBrandUuid;
if (oldFirebaseBrandId != null) {
newSupabaseBrandUuid = brandTranslationMap[oldFirebaseBrandId];
}
// 3. Controllo di sicurezza: se il brand non esiste su Supabase, saltiamo il record o mettiamo null?
// Se nella tua tabella 'model' il 'brand_id' NON PUÒ essere null, devi per forza avere un match!
if (newSupabaseBrandUuid == null && oldFirebaseBrandId != null) {
print(
"ATTENZIONE: Il modello ${data['nome']} ha un brand_id ($oldFirebaseBrandId) che non esiste su Supabase. Salto o metto null.",
);
continue; // Decommenta questo se vuoi saltare i modelli orfani
}
// Creiamo la riga per Supabase
supabaseModels.add({
'legacy_id': doc.id,
// ECCO LA CHIAVE ESTERNA TRADOTTA!
'brand_id': newSupabaseBrandUuid,
// Mappa gli altri campi
'name': (data['name'] as String).trim().toLowerCase(),
'name_with_brand': (data['nomeConMarca'] as String)
.toLowerCase()
.trim(),
});
}
// ==========================================================
// FASE 3: INVIO A SUPABASE
// ==========================================================
print("Sto per inviare ${supabaseModels.length} modelli a Supabase...");
await Supabase.instance.client
.from('model')
.upsert(supabaseModels, onConflict: 'legacy_id');
print("BOOM! Migrazione modelli completata con successo! 🚀");
} catch (e) {
print("Errore durante la migrazione dei modelli: $e");
}
}