189 lines
5.0 KiB
Dart
189 lines
5.0 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> 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);
|
|
}
|
|
}
|
|
}
|