import 'package:drift/drift.dart'; import 'app_database.dart'; class InsufficientStockException implements Exception { const InsufficientStockException(); @override String toString() => 'Not enough stock'; } class ProductNotFoundException implements Exception { final String productId; const ProductNotFoundException(this.productId); @override String toString() => 'Product not found: $productId'; } /// Atomic stock updates shared by the product and tab services, so they can /// run inside either one's transaction. extension StockQueries on AppDatabase { /// Products with infinite stock are only checked for existence. Future decreaseStock(String productId, int amount) async { _validateStockAmount(amount); final updatedRows = await (update(products)..where( (row) => row.id.equals(productId) & row.infiniteStock.not() & row.stockQuantity.isBiggerOrEqualValue(amount), )) .write( ProductsCompanion.custom( stockQuantity: products.stockQuantity - Variable(amount), ), ); if (updatedRows == 1) return; final product = await _findProduct(productId); if (!product.infiniteStock) throw const InsufficientStockException(); } Future increaseStock(String productId, int amount) async { _validateStockAmount(amount); final updatedRows = await (update(products)..where( (row) => row.id.equals(productId) & row.infiniteStock.not(), )) .write( ProductsCompanion.custom( stockQuantity: products.stockQuantity + Variable(amount), ), ); if (updatedRows != 1) await _findProduct(productId); } Future _findProduct(String productId) async { final product = await (select( products, )..where((row) => row.id.equals(productId))).getSingleOrNull(); if (product == null) throw ProductNotFoundException(productId); return product; } void _validateStockAmount(int amount) { if (amount <= 0) { throw ArgumentError.value(amount, 'amount', 'Must be greater than zero'); } } }