283 lines
8.6 KiB
Dart
283 lines
8.6 KiB
Dart
import 'package:path/path.dart' as p;
|
|
import 'package:sqflite/sqflite.dart';
|
|
|
|
import '../models/work_order.dart';
|
|
|
|
class AppDatabase {
|
|
AppDatabase._();
|
|
|
|
static final AppDatabase instance = AppDatabase._();
|
|
|
|
Database? _database;
|
|
|
|
Future<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
|
|
final databasePath = await getDatabasesPath();
|
|
_database = await openDatabase(
|
|
p.join(databasePath, 'ot_movil.db'),
|
|
version: 6,
|
|
onConfigure: (database) async {
|
|
await database.execute('PRAGMA foreign_keys = ON');
|
|
},
|
|
onCreate: (database, version) async {
|
|
await database.execute('''
|
|
CREATE TABLE work_orders (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
completed_at TEXT,
|
|
client TEXT NOT NULL DEFAULT '',
|
|
phone TEXT NOT NULL DEFAULT '',
|
|
address TEXT NOT NULL DEFAULT '',
|
|
labor_cost INTEGER NOT NULL DEFAULT 0,
|
|
recipient_email TEXT,
|
|
pdf_path TEXT
|
|
)
|
|
''');
|
|
await database.execute('''
|
|
CREATE TABLE work_tasks (
|
|
id TEXT PRIMARY KEY,
|
|
work_order_id TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
observation TEXT NOT NULL,
|
|
position INTEGER NOT NULL,
|
|
FOREIGN KEY (work_order_id)
|
|
REFERENCES work_orders (id) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
await database.execute('''
|
|
CREATE TABLE task_photos (
|
|
id TEXT PRIMARY KEY,
|
|
task_id TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
FOREIGN KEY (task_id)
|
|
REFERENCES work_tasks (id) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
await database.execute('''
|
|
CREATE TABLE task_parts (
|
|
id TEXT PRIMARY KEY,
|
|
task_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
price INTEGER NOT NULL,
|
|
position INTEGER NOT NULL,
|
|
FOREIGN KEY (task_id)
|
|
REFERENCES work_tasks (id) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
await database.execute(
|
|
'CREATE INDEX idx_tasks_order ON work_tasks(work_order_id)',
|
|
);
|
|
await database.execute(
|
|
'CREATE INDEX idx_photos_task ON task_photos(task_id)',
|
|
);
|
|
await database.execute(
|
|
'CREATE INDEX idx_parts_task ON task_parts(task_id)',
|
|
);
|
|
},
|
|
onUpgrade: (database, oldVersion, newVersion) async {
|
|
if (oldVersion < 2) {
|
|
await database.execute(
|
|
"ALTER TABLE work_orders "
|
|
"ADD COLUMN client TEXT NOT NULL DEFAULT ''",
|
|
);
|
|
await database.execute(
|
|
"ALTER TABLE work_orders "
|
|
"ADD COLUMN address TEXT NOT NULL DEFAULT ''",
|
|
);
|
|
}
|
|
if (oldVersion < 3) {
|
|
await database.execute('''
|
|
CREATE TABLE task_parts (
|
|
id TEXT PRIMARY KEY,
|
|
task_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
price INTEGER NOT NULL,
|
|
position INTEGER NOT NULL,
|
|
FOREIGN KEY (task_id)
|
|
REFERENCES work_tasks (id) ON DELETE CASCADE
|
|
)
|
|
''');
|
|
await database.execute(
|
|
'CREATE INDEX idx_parts_task ON task_parts(task_id)',
|
|
);
|
|
}
|
|
if (oldVersion < 4) {
|
|
await database.execute(
|
|
'ALTER TABLE work_orders '
|
|
'ADD COLUMN labor_cost INTEGER NOT NULL DEFAULT 0',
|
|
);
|
|
}
|
|
if (oldVersion < 5) {
|
|
await database.execute(
|
|
"ALTER TABLE work_orders "
|
|
"ADD COLUMN phone TEXT NOT NULL DEFAULT ''",
|
|
);
|
|
}
|
|
},
|
|
);
|
|
return _database!;
|
|
}
|
|
|
|
Future<List<WorkOrder>> getWorkOrders() async {
|
|
final db = await database;
|
|
final rows = await db.query('work_orders', orderBy: 'updated_at DESC');
|
|
|
|
final orders = <WorkOrder>[];
|
|
for (final row in rows) {
|
|
orders.add(await _workOrderFromRow(db, row));
|
|
}
|
|
return orders;
|
|
}
|
|
|
|
Future<WorkOrder?> getWorkOrder(String id) async {
|
|
final db = await database;
|
|
final rows = await db.query(
|
|
'work_orders',
|
|
where: 'id = ?',
|
|
whereArgs: [id],
|
|
limit: 1,
|
|
);
|
|
if (rows.isEmpty) return null;
|
|
return _workOrderFromRow(db, rows.first);
|
|
}
|
|
|
|
Future<WorkOrder> _workOrderFromRow(
|
|
DatabaseExecutor db,
|
|
Map<String, Object?> row,
|
|
) async {
|
|
final orderId = row['id']! as String;
|
|
final taskRows = await db.query(
|
|
'work_tasks',
|
|
where: 'work_order_id = ?',
|
|
whereArgs: [orderId],
|
|
orderBy: 'position ASC',
|
|
);
|
|
|
|
final tasks = <WorkTask>[];
|
|
for (final taskRow in taskRows) {
|
|
final taskId = taskRow['id']! as String;
|
|
final photoRows = await db.query(
|
|
'task_photos',
|
|
where: 'task_id = ?',
|
|
whereArgs: [taskId],
|
|
orderBy: 'rowid ASC',
|
|
);
|
|
final partRows = await db.query(
|
|
'task_parts',
|
|
where: 'task_id = ?',
|
|
whereArgs: [taskId],
|
|
orderBy: 'position ASC',
|
|
);
|
|
tasks.add(
|
|
WorkTask(
|
|
id: taskId,
|
|
title: taskRow['title']! as String,
|
|
observation: taskRow['observation']! as String,
|
|
position: taskRow['position']! as int,
|
|
photos: photoRows
|
|
.map(
|
|
(photoRow) => TaskPhoto(
|
|
id: photoRow['id']! as String,
|
|
path: photoRow['path']! as String,
|
|
kind: PhotoKind.fromDatabase(photoRow['kind']! as String),
|
|
),
|
|
)
|
|
.toList(),
|
|
parts: partRows
|
|
.map(
|
|
(partRow) => TaskPart(
|
|
id: partRow['id']! as String,
|
|
name: partRow['name']! as String,
|
|
price: partRow['price']! as int,
|
|
),
|
|
)
|
|
.toList(),
|
|
),
|
|
);
|
|
}
|
|
|
|
return WorkOrder(
|
|
id: orderId,
|
|
title: row['title']! as String,
|
|
status: WorkOrderStatus.fromDatabase(row['status']! as String),
|
|
createdAt: DateTime.parse(row['created_at']! as String),
|
|
updatedAt: DateTime.parse(row['updated_at']! as String),
|
|
completedAt: row['completed_at'] == null
|
|
? null
|
|
: DateTime.parse(row['completed_at']! as String),
|
|
details: WorkOrderDetails(
|
|
client: row['client']! as String,
|
|
phone: row['phone']! as String,
|
|
address: row['address']! as String,
|
|
),
|
|
laborCost: row['labor_cost']! as int,
|
|
recipientEmail: row['recipient_email'] as String?,
|
|
pdfPath: row['pdf_path'] as String?,
|
|
tasks: tasks,
|
|
);
|
|
}
|
|
|
|
Future<void> saveWorkOrder(WorkOrder order) async {
|
|
final db = await database;
|
|
await db.transaction((transaction) async {
|
|
await transaction.insert('work_orders', {
|
|
'id': order.id,
|
|
'title': order.title,
|
|
'status': order.status.databaseValue,
|
|
'created_at': order.createdAt.toIso8601String(),
|
|
'updated_at': order.updatedAt.toIso8601String(),
|
|
'completed_at': order.completedAt?.toIso8601String(),
|
|
'client': order.details.client,
|
|
'phone': order.details.phone,
|
|
'address': order.details.address,
|
|
'labor_cost': order.laborCost,
|
|
'recipient_email': order.recipientEmail,
|
|
'pdf_path': order.pdfPath,
|
|
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
|
|
|
await transaction.delete(
|
|
'work_tasks',
|
|
where: 'work_order_id = ?',
|
|
whereArgs: [order.id],
|
|
);
|
|
|
|
for (final task in order.tasks) {
|
|
await transaction.insert('work_tasks', {
|
|
'id': task.id,
|
|
'work_order_id': order.id,
|
|
'title': task.title,
|
|
'observation': task.observation,
|
|
'position': task.position,
|
|
});
|
|
for (final photo in task.photos) {
|
|
await transaction.insert('task_photos', {
|
|
'id': photo.id,
|
|
'task_id': task.id,
|
|
'kind': photo.kind.databaseValue,
|
|
'path': photo.path,
|
|
});
|
|
}
|
|
for (var index = 0; index < task.parts.length; index++) {
|
|
final part = task.parts[index];
|
|
await transaction.insert('task_parts', {
|
|
'id': part.id,
|
|
'task_id': task.id,
|
|
'name': part.name,
|
|
'price': part.price,
|
|
'position': index,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> deleteWorkOrder(String id) async {
|
|
final db = await database;
|
|
await db.delete('work_orders', where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
}
|