feat: add more detailed history and group per day
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:math';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -194,16 +195,18 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
final (name, priceInCents, stock) = demoProducts[i];
|
||||
final category = categories[i ~/ 5];
|
||||
|
||||
await database.into(database.products).insert(
|
||||
ProductsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
name: name,
|
||||
category: category,
|
||||
stockQuantity: stock,
|
||||
lowStockThreshold: 10,
|
||||
priceInCents: priceInCents,
|
||||
),
|
||||
);
|
||||
await database
|
||||
.into(database.products)
|
||||
.insert(
|
||||
ProductsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
name: name,
|
||||
category: category,
|
||||
stockQuantity: stock,
|
||||
lowStockThreshold: 10,
|
||||
priceInCents: priceInCents,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_lastAction = 'Seeded ${demoProducts.length} demo products';
|
||||
@@ -241,10 +244,7 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
final randomProducts = products.take(3).toList();
|
||||
|
||||
for (final product in randomProducts) {
|
||||
await barTabService.addProductToTab(
|
||||
tabId: tab.id,
|
||||
product: product,
|
||||
);
|
||||
await barTabService.addProductToTab(tabId: tab.id, product: product);
|
||||
}
|
||||
|
||||
_lastAction = 'Created test tab with ${randomProducts.length} items';
|
||||
@@ -262,16 +262,18 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
final uuid = const Uuid();
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
await database.into(database.products).insert(
|
||||
ProductsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
name: 'Test Product $i',
|
||||
category: 'Test Category ${i % 5}',
|
||||
stockQuantity: 50 + (i % 50),
|
||||
lowStockThreshold: 5,
|
||||
priceInCents: 100 + (i * 10),
|
||||
),
|
||||
);
|
||||
await database
|
||||
.into(database.products)
|
||||
.insert(
|
||||
ProductsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
name: 'Test Product $i',
|
||||
category: 'Test Category ${i % 5}',
|
||||
stockQuantity: 50 + (i % 50),
|
||||
lowStockThreshold: 5,
|
||||
priceInCents: 100 + (i * 10),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_lastAction = 'Added $count test products';
|
||||
@@ -341,59 +343,130 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final uuid = const Uuid();
|
||||
final random = Random();
|
||||
final today = DateTime.now();
|
||||
final startOfToday = DateTime(today.year, today.month, today.day);
|
||||
final customerNames = [
|
||||
'Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank',
|
||||
'Grace', 'Hank', 'Ivy', 'Jack', 'Kate', 'Leo',
|
||||
'Mia', 'Noah', 'Olivia', 'Pete', 'Quinn', 'Rose',
|
||||
'Sam', 'Tina', 'Umar', 'Vera', 'Wes', 'Xena',
|
||||
'Yves', 'Zara',
|
||||
'Alice',
|
||||
'Bob',
|
||||
'Charlie',
|
||||
'Diana',
|
||||
'Eve',
|
||||
'Frank',
|
||||
'Grace',
|
||||
'Hank',
|
||||
'Ivy',
|
||||
'Jack',
|
||||
'Kate',
|
||||
'Leo',
|
||||
'Mia',
|
||||
'Noah',
|
||||
'Olivia',
|
||||
'Pete',
|
||||
'Quinn',
|
||||
'Rose',
|
||||
'Sam',
|
||||
'Tina',
|
||||
'Umar',
|
||||
'Vera',
|
||||
'Wes',
|
||||
'Xena',
|
||||
'Yves',
|
||||
'Zara',
|
||||
];
|
||||
const paymentMethods = [PaymentMethod.cash, PaymentMethod.payconiq];
|
||||
final usedCustomers = <String>{};
|
||||
|
||||
await database.transaction(() async {
|
||||
for (var i = 0; i < count; i++) {
|
||||
final customer = customerNames[i % customerNames.length];
|
||||
final customer = customerNames[random.nextInt(customerNames.length)];
|
||||
usedCustomers.add(customer);
|
||||
final closedTabId = uuid.v4();
|
||||
final itemCount = 1 + (i % 5);
|
||||
final distinctProductCount = min(
|
||||
products.length,
|
||||
1 + random.nextInt(5),
|
||||
);
|
||||
final shuffledProducts = [...products]..shuffle(random);
|
||||
|
||||
final closedAt = DateTime.now().subtract(
|
||||
final orderDay = startOfToday.subtract(
|
||||
Duration(days: random.nextInt(60)),
|
||||
);
|
||||
final closedAt = DateTime(
|
||||
orderDay.year,
|
||||
orderDay.month,
|
||||
orderDay.day,
|
||||
12 + random.nextInt(10),
|
||||
random.nextInt(60),
|
||||
);
|
||||
|
||||
// A few mock tabs span multiple days so the history day dropdowns
|
||||
// also get realistic multi-day data.
|
||||
final spanDays = random.nextInt(5) == 0 ? 1 + random.nextInt(2) : 0;
|
||||
final firstPurchaseAt = closedAt.subtract(
|
||||
Duration(
|
||||
days: i % 90,
|
||||
hours: i % 24,
|
||||
minutes: i % 60,
|
||||
days: spanDays,
|
||||
hours: random.nextInt(4),
|
||||
minutes: random.nextInt(60),
|
||||
),
|
||||
);
|
||||
final purchaseWindowMinutes = closedAt
|
||||
.difference(firstPurchaseAt)
|
||||
.inMinutes;
|
||||
|
||||
await database.into(database.closedTabs).insert(
|
||||
ClosedTabsCompanion.insert(
|
||||
id: closedTabId,
|
||||
originalTabId: uuid.v4(),
|
||||
customerName: customer,
|
||||
closedAt: closedAt,
|
||||
paymentMethod: Value(paymentMethods[(i * 3) % paymentMethods.length].value),
|
||||
),
|
||||
);
|
||||
await database
|
||||
.into(database.closedTabs)
|
||||
.insert(
|
||||
ClosedTabsCompanion.insert(
|
||||
id: closedTabId,
|
||||
originalTabId: uuid.v4(),
|
||||
customerName: customer,
|
||||
closedAt: closedAt,
|
||||
paymentMethod: Value(
|
||||
paymentMethods[random.nextInt(paymentMethods.length)].value,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
for (var j = 0; j < itemCount; j++) {
|
||||
final product = products[(i + j) % products.length];
|
||||
final quantity = 1 + ((i * 7 + j * 13) % 6);
|
||||
for (var j = 0; j < distinctProductCount; j++) {
|
||||
final product = shuffledProducts[j];
|
||||
final quantity = 1 + random.nextInt(4);
|
||||
|
||||
await database.into(database.closedTabItems).insert(
|
||||
ClosedTabItemsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
closedTabId: closedTabId,
|
||||
productId: product.id,
|
||||
productName: product.name,
|
||||
quantity: quantity,
|
||||
unitPriceInCents: product.priceInCents,
|
||||
),
|
||||
);
|
||||
for (var k = 0; k < quantity; k++) {
|
||||
var purchasedAt = firstPurchaseAt.add(
|
||||
Duration(minutes: random.nextInt(purchaseWindowMinutes + 1)),
|
||||
);
|
||||
|
||||
if (spanDays > 0 && j == 0 && k == 0) {
|
||||
purchasedAt = firstPurchaseAt;
|
||||
} else if (spanDays > 0 &&
|
||||
j == distinctProductCount - 1 &&
|
||||
k == quantity - 1) {
|
||||
purchasedAt = closedAt.subtract(
|
||||
Duration(minutes: random.nextInt(30)),
|
||||
);
|
||||
}
|
||||
|
||||
await database
|
||||
.into(database.closedTabItems)
|
||||
.insert(
|
||||
ClosedTabItemsCompanion.insert(
|
||||
id: uuid.v4(),
|
||||
closedTabId: closedTabId,
|
||||
productId: product.id,
|
||||
productName: product.name,
|
||||
quantity: 1,
|
||||
unitPriceInCents: product.priceInCents,
|
||||
purchasedAt: Value(purchasedAt),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_lastAction = 'Generated $count mock orders across '
|
||||
'${customerNames.length} customers';
|
||||
_lastAction =
|
||||
'Generated $count mock orders across '
|
||||
'${usedCustomers.length} customers';
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/bar_tab.dart';
|
||||
import '../models/closed_tab.dart';
|
||||
import '../services/bar_tab_service.dart';
|
||||
|
||||
@@ -11,6 +12,7 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
static const int _pageSize = 20;
|
||||
|
||||
List<ClosedTab> _closedTabs = [];
|
||||
List<BarTab> _openTabs = [];
|
||||
List<String> _customerNames = [];
|
||||
String? _selectedCustomer;
|
||||
int _offset = 0;
|
||||
@@ -22,14 +24,14 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
String _searchQuery = '';
|
||||
|
||||
List<ClosedTab> get closedTabs {
|
||||
if (_searchQuery.isEmpty) return _closedTabs;
|
||||
|
||||
return _closedTabs
|
||||
.where(
|
||||
(tab) => tab.customerName.toLowerCase().contains(
|
||||
_searchQuery.toLowerCase(),
|
||||
),
|
||||
)
|
||||
.where((tab) => _matchesCustomer(tab.customerName))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<BarTab> get openTabs {
|
||||
return _openTabs
|
||||
.where((tab) => _matchesCustomer(tab.customerName))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -63,15 +65,19 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
_fetchPage(offset: 0),
|
||||
barTabService.getClosedTabCount(
|
||||
customerName: _selectedCustomer,
|
||||
),
|
||||
barTabService.getClosedTabCount(customerName: _selectedCustomer),
|
||||
barTabService.getDistinctCustomerNames(),
|
||||
barTabService.getOpenTabs(includeRemoved: true),
|
||||
]);
|
||||
|
||||
_closedTabs = results[0] as List<ClosedTab>;
|
||||
_closedTabs = (results[0] as List<ClosedTab>).toList()
|
||||
..sort(_compareClosedTabs);
|
||||
_totalCount = results[1] as int;
|
||||
_customerNames = results[2] as List<String>;
|
||||
_openTabs = (results[3] as List<BarTab>).toList()..sort(_compareOpenTabs);
|
||||
_customerNames = {
|
||||
...(results[2] as List<String>),
|
||||
..._openTabs.map((tab) => tab.customerName),
|
||||
}.toList()..sort();
|
||||
_offset = _closedTabs.length;
|
||||
} catch (e) {
|
||||
debugPrint('HistoryViewModel: load error: $e');
|
||||
@@ -91,7 +97,7 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
final more = await _fetchPage(offset: _offset);
|
||||
_closedTabs = [..._closedTabs, ...more];
|
||||
_closedTabs = [..._closedTabs, ...more]..sort(_compareClosedTabs);
|
||||
_offset = _closedTabs.length;
|
||||
} catch (e) {
|
||||
debugPrint('HistoryViewModel: loadMore error: $e');
|
||||
@@ -110,11 +116,35 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
int _compareClosedTabs(ClosedTab a, ClosedTab b) {
|
||||
final byClosedAt = b.closedAt.compareTo(a.closedAt);
|
||||
if (byClosedAt != 0) return byClosedAt;
|
||||
|
||||
return b.id.compareTo(a.id);
|
||||
}
|
||||
|
||||
int _compareOpenTabs(BarTab a, BarTab b) {
|
||||
final byOpenedAt = b.openedAt.compareTo(a.openedAt);
|
||||
if (byOpenedAt != 0) return byOpenedAt;
|
||||
|
||||
return b.id.compareTo(a.id);
|
||||
}
|
||||
|
||||
void search(String query) {
|
||||
_searchQuery = query;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool _matchesCustomer(String customerName) {
|
||||
if (_selectedCustomer != null && customerName != _selectedCustomer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_searchQuery.isEmpty) return true;
|
||||
|
||||
return customerName.toLowerCase().contains(_searchQuery.toLowerCase());
|
||||
}
|
||||
|
||||
Future<void> filterByCustomer(String? customerName) async {
|
||||
_selectedCustomer = customerName;
|
||||
_searchQuery = '';
|
||||
@@ -123,4 +153,4 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
|
||||
await load();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user