feat: make server own products

This commit is contained in:
2026-10-21 12:10:42 +02:00
parent 93acfe15a9
commit b15d94a805
21 changed files with 1717 additions and 106 deletions
+97 -66
View File
@@ -461,68 +461,87 @@ class DriftBarTabService implements BarTabService {
final tabRow = await _getTabRow(tabId);
if (tabRow == null) throw TabNotFoundException(tabId);
// A merge on the server can leave two lines of one product at
// different prices; new drinks go on the oldest
final existingItem =
await (database.select(database.tabItems)
..where(
(item) =>
item.tabId.equals(tabId) &
item.productId.equals(product.id),
)
..orderBy([(item) => OrderingTerm.asc(item.createdAt)])
..limit(1))
.getSingleOrNull();
if (existingItem != null) {
await (database.update(
database.tabItems,
)..where((item) => item.id.equals(existingItem.id))).write(
TabItemsCompanion(quantity: Value(existingItem.quantity + 1)),
);
await _recordPurchase(existingItem.id);
// The line keeps the price it was first added at
await _recordLineChange(
tabId: tabId,
customerName: tabRow.customerName,
lineId: existingItem.id,
productId: existingItem.productId,
productName: existingItem.productName,
unitPriceInCents: existingItem.unitPriceInCents,
delta: 1,
);
} else {
final tabItemId = _uuid.v4();
await database
.into(database.tabItems)
.insert(
TabItemsCompanion.insert(
id: tabItemId,
tabId: tabId,
productId: product.id,
productName: product.name,
quantity: 1,
unitPriceInCents: product.priceInCents,
createdAt: DateTime.now(),
),
);
await _recordPurchase(tabItemId);
await _recordLineChange(
tabId: tabId,
customerName: tabRow.customerName,
lineId: tabItemId,
productId: product.id,
productName: product.name,
unitPriceInCents: product.priceInCents,
delta: 1,
);
}
await _addDrinks(
tabRow,
productId: product.id,
productName: product.name,
unitPriceInCents: product.priceInCents,
quantity: 1,
);
await _incrementTabVersion(tabId);
});
}
/// Puts [quantity] drinks on the tab, charged [unitPriceInCents]: on the
/// line of that product, name and price, or a new line when there is none.
/// Every new drink goes through here, so it is always charged the current
/// price: after a price or name change on the website the drinks already
/// on the tab keep what they were rung up at, and new ones start a new
/// line. Of several matching lines (a merge on the server) the oldest gets
/// them.
Future<void> _addDrinks(
BarTabRow tabRow, {
required String productId,
required String productName,
required int unitPriceInCents,
required int quantity,
}) async {
final existingItem =
await (database.select(database.tabItems)
..where(
(item) =>
item.tabId.equals(tabRow.id) &
item.productId.equals(productId) &
item.unitPriceInCents.equals(unitPriceInCents) &
item.productName.equals(productName),
)
..orderBy([(item) => OrderingTerm.asc(item.createdAt)])
..limit(1))
.getSingleOrNull();
final String lineId;
if (existingItem != null) {
lineId = existingItem.id;
await (database.update(
database.tabItems,
)..where((item) => item.id.equals(lineId))).write(
TabItemsCompanion(quantity: Value(existingItem.quantity + quantity)),
);
} else {
lineId = _uuid.v4();
await database
.into(database.tabItems)
.insert(
TabItemsCompanion.insert(
id: lineId,
tabId: tabRow.id,
productId: productId,
productName: productName,
quantity: quantity,
unitPriceInCents: unitPriceInCents,
createdAt: DateTime.now(),
),
);
}
for (var index = 0; index < quantity; index++) {
await _recordPurchase(lineId);
}
await _recordLineChange(
tabId: tabRow.id,
customerName: tabRow.customerName,
lineId: lineId,
productId: productId,
productName: productName,
unitPriceInCents: unitPriceInCents,
delta: quantity,
);
}
/// Taking drinks off changes [tabItemId] itself. Adding is ringing up new
/// drinks of its product, at the current price: on this line while its
/// price and name are still current, else on the line at the current price
/// (see [_addDrinks]). Returns how many were added (positive) or taken off.
@override
Future<int> adjustTabItemQuantity({
required String tabItemId,
@@ -537,19 +556,31 @@ class DriftBarTabService implements BarTabService {
if (item == null) return 0;
if (delta > 0) {
final tabRow = await _getTabRow(item.tabId);
if (tabRow == null) return 0;
// Hidden products still count: the drink is on the tab already
final product = await (database.select(
database.products,
)..where((row) => row.id.equals(item.productId))).getSingleOrNull();
await _addDrinks(
tabRow,
productId: item.productId,
productName: product?.name ?? item.productName,
unitPriceInCents: product?.priceInCents ?? item.unitPriceInCents,
quantity: delta,
);
await _incrementTabVersion(item.tabId);
return delta;
}
final newQuantity = max(item.quantity + delta, 0);
final actualDelta = newQuantity - item.quantity;
if (actualDelta == 0) return 0;
if (actualDelta > 0) {
for (var index = 0; index < actualDelta; index++) {
await _recordPurchase(item.id);
}
} else {
await _removeLatestPurchases(item.id, -actualDelta);
}
await _removeLatestPurchases(item.id, -actualDelta);
await (database.update(database.tabItems)
..where((row) => row.id.equals(tabItemId)))
.write(TabItemsCompanion(quantity: Value(newQuantity)));