Files
kooltab/lib/viewmodels/bar_screen_view_model.dart

220 lines
5.9 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../models/payment_method.dart';
import '../models/bar_tab.dart';
import '../models/product.dart';
import '../models/tab_item.dart';
import '../services/bar_tab_service.dart';
import '../services/product_service.dart';
import 'inventory_view_model.dart';
class BarScreenViewModel extends ChangeNotifier {
final BarTabService barTabService;
final InventoryViewModel inventory;
BarScreenViewModel({required this.barTabService, required this.inventory});
List<BarTab> _tabs = [];
String? _selectedTabId;
bool _isLoading = false;
bool _hasLoaded = false;
String? _errorMessage;
List<BarTab> get tabs => _tabs;
String? get selectedTabId => _selectedTabId;
bool get isLoading => _isLoading;
bool get hasLoaded => _hasLoaded;
String? get errorMessage => _errorMessage;
BarTab? get selectedTab {
if (_selectedTabId == null) return null;
try {
return _tabs.firstWhere((tab) => tab.id == _selectedTabId);
} catch (e) {
debugPrint('BarScreenViewModel: error finding selected tab: $e');
return null;
}
}
Future<void> ensureLoaded() async {
if (_hasLoaded || _isLoading) return;
await load();
}
Future<void> load() async {
if (_isLoading) return;
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
_tabs = await barTabService.getOpenTabs();
if (_selectedTabId == null ||
!_tabs.any((tab) => tab.id == _selectedTabId)) {
_selectedTabId = _tabs.isEmpty ? null : _tabs.first.id;
}
} catch (e) {
debugPrint('BarScreenViewModel: load error: $e');
_errorMessage = 'Could not load bar screen.';
} finally {
_hasLoaded = true;
_isLoading = false;
notifyListeners();
}
}
void selectTab(String tabId) {
_selectedTabId = tabId;
notifyListeners();
}
Future<void> createTab(String customerName) async {
try {
final tab = await barTabService.createTab(customerName: customerName);
_selectedTabId = tab.id;
await _reloadTabs();
} catch (e, stack) {
debugPrint('BarScreenViewModel: createTab error: $e');
_errorMessage = 'Could not create tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
}
}
Future<void> renameTab(String tabId, String customerName) async {
try {
await barTabService.renameTab(tabId: tabId, customerName: customerName);
await _reloadTabs();
} catch (e, stack) {
debugPrint('BarScreenViewModel: renameTab error: $e');
_errorMessage = 'Could not rename tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> deleteTab(String tabId) async {
try {
final deletedItems = await barTabService.deleteTab(tabId);
for (final item in deletedItems) {
inventory.applyStockDelta(item.productId, item.quantity);
}
await _reloadTabs();
} catch (e, stack) {
debugPrint('BarScreenViewModel: deleteTab error: $e');
_errorMessage = 'Could not delete tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> addProductToSelectedTab(Product product) async {
final tab = selectedTab;
if (tab == null) return;
try {
await barTabService.addProductToTab(tabId: tab.id, product: product);
inventory.applyStockDelta(product.id, -1);
await _reloadTabs();
} catch (e, stack) {
if (e is InsufficientStockException) rethrow;
debugPrint('BarScreenViewModel: addProductToSelectedTab error: $e');
_errorMessage = 'Could not add product to tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> changeItemQuantity(TabItem item, int delta) async {
if (delta == 0) return;
try {
final actualDelta = await barTabService.adjustTabItemQuantity(
tabItemId: item.id,
delta: delta,
);
inventory.applyStockDelta(item.productId, -actualDelta);
await _reloadTabs();
} catch (e, stack) {
if (e is InsufficientStockException) rethrow;
debugPrint('BarScreenViewModel: changeItemQuantity error: $e');
_errorMessage = 'Could not update item quantity.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> closeSelectedTab({
PaymentMethod paymentMethod = PaymentMethod.cash,
}) async {
final tab = selectedTab;
if (tab == null) return;
try {
await barTabService.closeTab(tab.id, paymentMethod: paymentMethod);
await _reloadTabs();
} catch (e, stack) {
debugPrint('BarScreenViewModel: closeSelectedTab error: $e');
_errorMessage = 'Could not close tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> closeTab(
String tabId, {
PaymentMethod paymentMethod = PaymentMethod.cash,
}) async {
try {
await barTabService.closeTab(tabId, paymentMethod: paymentMethod);
await _reloadTabs();
} catch (e, stack) {
debugPrint('BarScreenViewModel: closeTab error: $e');
_errorMessage = 'Could not close tab.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
rethrow;
}
}
Future<void> _reloadTabs() async {
try {
_tabs = await barTabService.getOpenTabs();
if (_selectedTabId == null ||
!_tabs.any((tab) => tab.id == _selectedTabId)) {
_selectedTabId = _tabs.isEmpty ? null : _tabs.first.id;
}
notifyListeners();
} catch (e, stack) {
debugPrint('BarScreenViewModel: reloadTabs error: $e');
_errorMessage = 'Could not reload tabs.';
notifyListeners();
Sentry.captureException(e, stackTrace: stack);
}
}
}