Files
receipity/lib/services/product_repository.dart
T
2026-08-29 11:23:24 +02:00

94 lines
2.4 KiB
Dart

/// Local SQLite store of confirmed products.
library;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import '../models/line_item.dart';
import '../models/product.dart';
class ProductRepository {
Database? _db;
Future<Database> get _database async {
_db ??= await _open();
return _db!;
}
Future<Database> _open() async {
final dir = await getApplicationDocumentsDirectory();
return openDatabase(
p.join(dir.path, 'receipity.db'),
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
normalized_name TEXT NOT NULL UNIQUE,
last_price REAL NOT NULL,
times_seen INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
''');
},
);
}
static String normalizeName(String name) =>
name.trim().toUpperCase().replaceAll(RegExp(r'\s+'), ' ');
Future<void> upsertItems(List<LineItem> items) async {
final db = await _database;
final now = DateTime.now().toIso8601String();
await db.transaction((txn) async {
for (final item in items) {
final key = normalizeName(item.name);
if (key.isEmpty) continue;
final existing = await txn.query(
'products',
where: 'normalized_name = ?',
whereArgs: [key],
limit: 1,
);
if (existing.isEmpty) {
await txn.insert('products', {
'name': item.name.trim(),
'normalized_name': key,
'last_price': item.price,
'times_seen': 1,
'created_at': now,
'updated_at': now,
});
} else {
await txn.update(
'products',
{
'name': item.name.trim(),
'last_price': item.price,
'times_seen': (existing.first['times_seen']! as int) + 1,
'updated_at': now,
},
where: 'id = ?',
whereArgs: [existing.first['id']],
);
}
}
});
}
Future<List<Product>> getAll() async {
final db = await _database;
final rows = await db.query(
'products',
orderBy: 'name COLLATE NOCASE ASC',
);
return rows.map(Product.fromMap).toList();
}
}