feat: add historical data

This commit is contained in:
2026-10-19 23:31:54 +02:00
parent b0074aeb3f
commit 5754ae742d
10 changed files with 1211 additions and 4 deletions
+97 -2
View File
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:math';
import 'package:drift/drift.dart';
@@ -10,6 +11,7 @@ import '../models/closed_tab.dart';
import '../models/closed_tab_item.dart';
import '../models/payment_method.dart';
import '../models/product.dart';
import '../models/tab_event.dart';
import '../models/tab_item.dart';
import '../models/tab_item_purchase.dart';
@@ -69,11 +71,13 @@ abstract class BarTabService {
/// The tab itself stays open under the same customer name.
///
/// Throws [TabVersionConflictException] when [expectedVersion] is given and
/// the tab changed since.
/// the tab changed since. [commandId] is the remote command that requested
/// the settlement, if any.
Future<void> closeTab(
String tabId, {
PaymentMethod paymentMethod = PaymentMethod.cash,
int? expectedVersion,
String? commandId,
});
Future<List<ClosedTab>> getClosedTabs();
@@ -91,9 +95,11 @@ abstract class BarTabService {
class DriftBarTabService implements BarTabService {
final AppDatabase database;
final TabEventFactory eventFactory;
final _uuid = const Uuid();
DriftBarTabService({required this.database});
DriftBarTabService({required this.database, TabEventFactory? eventFactory})
: eventFactory = eventFactory ?? TabEventFactory();
TabItemPurchase _mapPurchaseRow(TabItemPurchaseRow row) {
return TabItemPurchase(
@@ -255,6 +261,21 @@ class DriftBarTabService implements BarTabService {
)..where((item) => item.tabId.equals(tabId))).go();
}
/// Queues [event] in the outbox. Call it inside the transaction that makes
/// the change, so the change and its event are stored together or not at
/// all.
Future<void> _recordEvent(TabEvent event) async {
await database
.into(database.tabEvents)
.insert(
TabEventsCompanion.insert(
id: event.id,
payload: jsonEncode(event.payload),
createdAt: DateTime.now(),
),
);
}
Future<void> _incrementTabVersion(String tabId) async {
await (database.update(
database.barTabs,
@@ -341,6 +362,15 @@ class DriftBarTabService implements BarTabService {
for (final item in tab.items) {
await database.increaseStock(item.productId, item.quantity);
await _recordEvent(
eventFactory.lineRemoved(
tabId: tab.id,
customerName: tab.customerName,
productName: item.productName,
unitPriceInCents: item.unitPriceInCents,
quantity: item.quantity,
),
);
}
await _deleteItemsOfTab(tabId);
@@ -356,6 +386,9 @@ class DriftBarTabService implements BarTabService {
required Product product,
}) async {
await database.transaction(() async {
final tabRow = await _getTabRow(tabId);
if (tabRow == null) throw TabNotFoundException(tabId);
await database.decreaseStock(product.id, 1);
final existingItem =
@@ -373,6 +406,16 @@ class DriftBarTabService implements BarTabService {
TabItemsCompanion(quantity: Value(existingItem.quantity + 1)),
);
await _recordPurchase(existingItem.id);
// The line keeps the price it was first added at
await _recordEvent(
eventFactory.lineAdded(
tabId: tabId,
customerName: tabRow.customerName,
productName: existingItem.productName,
unitPriceInCents: existingItem.unitPriceInCents,
quantity: 1,
),
);
} else {
final tabItemId = _uuid.v4();
@@ -390,6 +433,15 @@ class DriftBarTabService implements BarTabService {
),
);
await _recordPurchase(tabItemId);
await _recordEvent(
eventFactory.lineAdded(
tabId: tabId,
customerName: tabRow.customerName,
productName: product.name,
unitPriceInCents: product.priceInCents,
quantity: 1,
),
);
}
await _incrementTabVersion(tabId);
@@ -429,6 +481,20 @@ class DriftBarTabService implements BarTabService {
..where((row) => row.id.equals(tabItemId)))
.write(TabItemsCompanion(quantity: Value(newQuantity)));
final tabRow = await _getTabRow(item.tabId);
final lineEvent = actualDelta > 0
? eventFactory.lineAdded
: eventFactory.lineRemoved;
await _recordEvent(
lineEvent(
tabId: item.tabId,
customerName: tabRow?.customerName ?? '',
productName: item.productName,
unitPriceInCents: item.unitPriceInCents,
quantity: actualDelta.abs(),
),
);
await _incrementTabVersion(item.tabId);
return actualDelta;
@@ -440,6 +506,7 @@ class DriftBarTabService implements BarTabService {
String tabId, {
PaymentMethod paymentMethod = PaymentMethod.cash,
int? expectedVersion,
String? commandId,
}) async {
await database.transaction(() async {
final tabRow = await _getTabRow(tabId);
@@ -488,6 +555,34 @@ class DriftBarTabService implements BarTabService {
]);
});
// One settlement, not a removal per line: these drinks were sold
final settledItems = [
for (final item in items)
if (item.quantity > 0) item,
];
if (settledItems.isNotEmpty) {
await _recordEvent(
eventFactory.settled(
tabId: tabId,
customerName: tabRow.customerName,
amountInCents: settledItems.fold(
0,
(total, item) => total + item.lineTotalInCents,
),
paymentMethod: paymentMethod,
commandId: commandId,
items: [
for (final item in settledItems)
(
productName: item.productName,
quantity: item.quantity,
unitPriceInCents: item.unitPriceInCents,
),
],
),
);
}
await _deleteItemsOfTab(tabId);
await _incrementTabVersion(tabId);
});