211 lines
7.2 KiB
Dart
211 lines
7.2 KiB
Dart
import 'package:sqflite/sqflite.dart';
|
|
import 'package:path/path.dart';
|
|
import '../models/servicio.dart';
|
|
import '../models/factura.dart';
|
|
import '../models/configuracion.dart';
|
|
|
|
class DatabaseHelper {
|
|
static final DatabaseHelper instance = DatabaseHelper._init();
|
|
static Database? _database;
|
|
|
|
DatabaseHelper._init();
|
|
|
|
Future<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
_database = await _initDB('hogarcontrol.db');
|
|
return _database!;
|
|
}
|
|
|
|
Future<Database> _initDB(String filePath) async {
|
|
final dbPath = await getDatabasesPath();
|
|
final path = join(dbPath, filePath);
|
|
return await openDatabase(path, version: 3, onCreate: _createDB, onUpgrade: _upgradeDB, onConfigure: _onConfigure);
|
|
}
|
|
|
|
Future _onConfigure(Database db) async {
|
|
await db.execute('PRAGMA foreign_keys = ON');
|
|
}
|
|
|
|
Future _upgradeDB(Database db, int oldVersion, int newVersion) async {
|
|
if (oldVersion < 3) {
|
|
try {
|
|
await db.execute(
|
|
"ALTER TABLE servicios ADD COLUMN es_recurrente INTEGER NOT NULL DEFAULT 0");
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
Future _createDB(Database db, int version) async {
|
|
await db.execute('''
|
|
CREATE TABLE servicios (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
nombre TEXT NOT NULL,
|
|
icono TEXT NOT NULL,
|
|
color INTEGER NOT NULL,
|
|
dia_vencimiento INTEGER NOT NULL,
|
|
fecha_creacion TEXT NOT NULL,
|
|
es_recurrente INTEGER NOT NULL DEFAULT 0
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE facturas (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
servicio_id INTEGER NOT NULL,
|
|
monto REAL NOT NULL,
|
|
fecha_emision TEXT NOT NULL,
|
|
fecha_vencimiento TEXT NOT NULL,
|
|
estado TEXT NOT NULL DEFAULT 'Pendiente',
|
|
nota TEXT,
|
|
fecha_pago TEXT,
|
|
FOREIGN KEY (servicio_id) REFERENCES servicios(id) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE configuracion (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
dias_recordatorio TEXT NOT NULL DEFAULT '7,3,1,0',
|
|
tema TEXT NOT NULL DEFAULT 'claro',
|
|
moneda TEXT NOT NULL DEFAULT 'USD'
|
|
)
|
|
''');
|
|
}
|
|
|
|
Future<int> insertServicio(Servicio servicio) async {
|
|
final db = await database;
|
|
return await db.insert('servicios', servicio.toMap()..remove('id'));
|
|
}
|
|
|
|
Future<List<Servicio>> getServicios() async {
|
|
final db = await database;
|
|
final maps = await db.query('servicios', orderBy: 'nombre ASC');
|
|
return maps.map((map) => Servicio.fromMap(map)).toList();
|
|
}
|
|
|
|
Future<int> updateServicio(Servicio servicio) async {
|
|
final db = await database;
|
|
return await db.update('servicios', servicio.toMap(), where: 'id = ?', whereArgs: [servicio.id]);
|
|
}
|
|
|
|
Future<int> deleteServicio(int id) async {
|
|
final db = await database;
|
|
return await db.delete('servicios', where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
|
|
Future<Servicio?> getServicio(int id) async {
|
|
final db = await database;
|
|
final maps = await db.query('servicios', where: 'id = ?', whereArgs: [id], limit: 1);
|
|
if (maps.isEmpty) return null;
|
|
return Servicio.fromMap(maps.first);
|
|
}
|
|
|
|
Future<int> insertFactura(Factura factura) async {
|
|
final db = await database;
|
|
return await db.insert('facturas', factura.toMap()..remove('id'));
|
|
}
|
|
|
|
Future<List<Factura>> getFacturas({String? estado, int? servicioId}) async {
|
|
final db = await database;
|
|
String? where;
|
|
List<dynamic>? whereArgs;
|
|
if (estado != null && servicioId != null) {
|
|
where = 'estado = ? AND servicio_id = ?';
|
|
whereArgs = [estado, servicioId];
|
|
} else if (estado != null) {
|
|
where = 'estado = ?';
|
|
whereArgs = [estado];
|
|
} else if (servicioId != null) {
|
|
where = 'servicio_id = ?';
|
|
whereArgs = [servicioId];
|
|
}
|
|
final maps = await db.query('facturas', where: where, whereArgs: whereArgs, orderBy: 'fecha_vencimiento DESC');
|
|
return maps.map((map) => Factura.fromMap(map)).toList();
|
|
}
|
|
|
|
Future<List<Factura>> getFacturasVencidas() async {
|
|
final db = await database;
|
|
final hoy = DateTime.now().toIso8601String().split('T')[0];
|
|
final maps = await db.query('facturas',
|
|
where: 'estado = ? AND fecha_vencimiento < ?', whereArgs: ['Pendiente', hoy], orderBy: 'fecha_vencimiento ASC');
|
|
return maps.map((map) => Factura.fromMap(map)).toList();
|
|
}
|
|
|
|
Future<List<Factura>> getFacturasPorMes(int year, int month) async {
|
|
final db = await database;
|
|
final mes = month.toString().padLeft(2, '0');
|
|
final maps = await db.query('facturas',
|
|
where: "fecha_vencimiento LIKE ?", whereArgs: ['$year-$mes%'], orderBy: 'fecha_vencimiento ASC');
|
|
return maps.map((map) => Factura.fromMap(map)).toList();
|
|
}
|
|
|
|
Future<List<Factura>> getFacturasPorRango(String inicio, String fin) async {
|
|
final db = await database;
|
|
final maps = await db.query('facturas',
|
|
where: 'fecha_vencimiento BETWEEN ? AND ?', whereArgs: [inicio, fin], orderBy: 'fecha_vencimiento ASC');
|
|
return maps.map((map) => Factura.fromMap(map)).toList();
|
|
}
|
|
|
|
Future<double> getTotalGastadoMes(int year, int month) async {
|
|
final db = await database;
|
|
final mes = month.toString().padLeft(2, '0');
|
|
final result = await db.rawQuery(
|
|
"SELECT COALESCE(SUM(monto), 0) as total FROM facturas WHERE estado = 'Pagada' AND fecha_pago LIKE ?",
|
|
['$year-$mes%']);
|
|
return (result.first['total'] as num).toDouble();
|
|
}
|
|
|
|
Future<Map<String, double>> getGastosPorServicio(int year, int month) async {
|
|
final db = await database;
|
|
final mes = month.toString().padLeft(2, '0');
|
|
final result = await db.rawQuery('''
|
|
SELECT s.nombre, COALESCE(SUM(f.monto), 0) as total
|
|
FROM facturas f
|
|
JOIN servicios s ON f.servicio_id = s.id
|
|
WHERE f.estado = 'Pagada' AND f.fecha_pago LIKE ?
|
|
GROUP BY s.nombre
|
|
''', ['$year-$mes%']);
|
|
final map = <String, double>{};
|
|
for (final row in result) {
|
|
map[row['nombre'] as String] = (row['total'] as num).toDouble();
|
|
}
|
|
return map;
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>> getGastoMensual(int year) async {
|
|
final db = await database;
|
|
return await db.rawQuery('''
|
|
SELECT strftime('%m', fecha_pago) as mes, COALESCE(SUM(monto), 0) as total
|
|
FROM facturas
|
|
WHERE estado = 'Pagada' AND fecha_pago LIKE ?
|
|
GROUP BY mes
|
|
ORDER BY mes
|
|
''', ['$year-%']);
|
|
}
|
|
|
|
Future<int> updateFactura(Factura factura) async {
|
|
final db = await database;
|
|
return await db.update('facturas', factura.toMap(), where: 'id = ?', whereArgs: [factura.id]);
|
|
}
|
|
|
|
Future<int> deleteFactura(int id) async {
|
|
final db = await database;
|
|
return await db.delete('facturas', where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
|
|
Future<Configuracion?> getConfiguracion() async {
|
|
final db = await database;
|
|
final maps = await db.query('configuracion', limit: 1);
|
|
if (maps.isEmpty) return null;
|
|
return Configuracion.fromMap(maps.first);
|
|
}
|
|
|
|
Future<int> saveConfiguracion(Configuracion config) async {
|
|
final db = await database;
|
|
final existing = await getConfiguracion();
|
|
if (existing != null) {
|
|
return await db.update('configuracion', config.toMap()..remove('id'),
|
|
where: 'id = ?', whereArgs: [existing.id]);
|
|
}
|
|
return await db.insert('configuracion', config.toMap()..remove('id'));
|
|
}
|
|
}
|