feat: make server owner of tabs

This commit is contained in:
2026-10-20 16:15:14 +02:00
parent 5754ae742d
commit 93acfe15a9
26 changed files with 3654 additions and 389 deletions
+62 -30
View File
@@ -4,9 +4,9 @@ import 'package:uuid/uuid.dart';
import '../database/app_database.dart';
import '../database/stock_queries.dart';
import '../models/product.dart';
import '../models/tab_event.dart';
export '../database/stock_queries.dart'
show InsufficientStockException, ProductNotFoundException;
export '../database/stock_queries.dart' show ProductNotFoundException;
abstract class ProductService {
/// Active products, re-emitted whenever the products table changes.
@@ -37,9 +37,11 @@ abstract class ProductService {
class DriftProductService implements ProductService {
final AppDatabase database;
final TabEventFactory eventFactory;
final _uuid = const Uuid();
DriftProductService({required this.database});
DriftProductService({required this.database, TabEventFactory? eventFactory})
: eventFactory = eventFactory ?? TabEventFactory();
Product _mapRowToProduct(ProductRow row) {
return Product(
@@ -95,39 +97,69 @@ class DriftProductService implements ProductService {
required int priceInCents,
required String? imagePath,
}) async {
await database
.into(database.products)
.insert(
ProductsCompanion.insert(
id: _uuid.v4(),
name: name,
category: category,
stockQuantity: stockQuantity,
lowStockThreshold: lowStockThreshold,
infiniteStock: Value(infiniteStock),
priceInCents: priceInCents,
imagePath: Value(imagePath),
),
final id = _uuid.v4();
await database.transaction(() async {
await database
.into(database.products)
.insert(
ProductsCompanion.insert(
id: id,
name: name,
category: category,
stockQuantity: stockQuantity,
lowStockThreshold: lowStockThreshold,
infiniteStock: Value(infiniteStock),
priceInCents: priceInCents,
imagePath: Value(imagePath),
),
);
// The server starts a product it doesn't know as untracked
if (!infiniteStock) {
await database.queueStockSet(
eventFactory.stockSet(productId: id, quantity: stockQuantity),
);
}
});
}
/// A changed stock or tracking is queued for the server as a count.
@override
Future<void> updateProduct(Product product) async {
final query = database.update(database.products)
..where((row) => row.id.equals(product.id));
await database.transaction(() async {
final current = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingleOrNull();
await query.write(
ProductsCompanion(
name: Value(product.name),
category: Value(product.category),
stockQuantity: Value(product.stockQuantity),
lowStockThreshold: Value(product.lowStockThreshold),
infiniteStock: Value(product.infiniteStock),
priceInCents: Value(product.priceInCents),
imagePath: Value(product.imagePath),
active: Value(product.active),
),
);
await (database.update(
database.products,
)..where((row) => row.id.equals(product.id))).write(
ProductsCompanion(
name: Value(product.name),
category: Value(product.category),
stockQuantity: Value(product.stockQuantity),
lowStockThreshold: Value(product.lowStockThreshold),
infiniteStock: Value(product.infiniteStock),
priceInCents: Value(product.priceInCents),
imagePath: Value(product.imagePath),
active: Value(product.active),
),
);
if (current == null) return;
final stockChanged = product.infiniteStock
? !current.infiniteStock
: current.infiniteStock ||
current.stockQuantity != product.stockQuantity;
if (stockChanged) {
await database.queueStockSet(
eventFactory.stockSet(
productId: product.id,
quantity: product.infiniteStock ? null : product.stockQuantity,
),
);
}
});
}
/// Only hidden, since open tabs and history still refer to it.