import 'dart:convert'; import 'package:sqflite/sqflite.dart'; import '../../domain/entities/sync_event.dart'; import 'app_database.dart'; /// Persists the history of this terminal's conversations with the server. /// /// Held on disk rather than in memory because it is the only record of a sync /// that survives the bills themselves: once the server accepts an order the row /// is deleted from the terminal, so without this the evidence that it ever went /// up disappears at the next restart. class SyncLogDao { const SyncLogDao(this._db); final Database _db; Future insert(SyncEvent e) async { await _db.insert( Tables.syncLog, { 'id': e.id, 'type': e.type.name, 'status': e.status.name, 'created_at': e.createdAt.millisecondsSinceEpoch, 'synced_at': e.syncedAt?.millisecondsSinceEpoch, 'summary': e.summary, 'error': e.error, 'attempts': e.attempts, 'payload_json': e.payload.isEmpty ? null : jsonEncode(e.payload), }, conflictAlgorithm: ConflictAlgorithm.replace, ); } /// Newest first, which is the order the events log renders. Future> recent({int limit = 200}) async { final rows = await _db.query( Tables.syncLog, orderBy: 'created_at DESC', limit: limit, ); return rows.map(_fromRow).toList(); } /// Keeps the log from growing without bound on a terminal that runs for /// months. Only ever trims the oldest entries. Future trim({int keep = 500}) async { await _db.rawDelete( 'DELETE FROM ${Tables.syncLog} WHERE id NOT IN (' 'SELECT id FROM ${Tables.syncLog} ORDER BY created_at DESC LIMIT ?)', [keep], ); } SyncEvent _fromRow(Map r) { final payload = r['payload_json'] as String?; return SyncEvent( id: r['id']! as String, type: SyncEventType.values.byName(r['type']! as String), status: SyncStatus.values.byName(r['status']! as String), createdAt: DateTime.fromMillisecondsSinceEpoch(r['created_at']! as int), summary: r['summary']! as String, payload: payload == null ? const {} : (jsonDecode(payload) as Map).cast(), syncedAt: r['synced_at'] == null ? null : DateTime.fromMillisecondsSinceEpoch(r['synced_at']! as int), error: r['error'] as String?, attempts: (r['attempts'] as int?) ?? 0, ); } }