feat: add remote products and qr code for payconic

This commit is contained in:
2026-10-22 00:10:26 +02:00
parent b15d94a805
commit 71af6f9ffe
31 changed files with 2053 additions and 179 deletions
+5
View File
@@ -170,6 +170,9 @@ class ServerProduct {
/// Deleted on the website: hidden, kept for open tabs and the history.
final bool archived;
/// The picture, at `/images/<imageId>`; null: none.
final String? imageId;
const ServerProduct({
required this.id,
required this.name,
@@ -177,6 +180,7 @@ class ServerProduct {
required this.priceInCents,
this.lowStockThreshold,
this.archived = false,
this.imageId,
});
factory ServerProduct.fromJson(Map<String, dynamic> json) {
@@ -187,6 +191,7 @@ class ServerProduct {
priceInCents: json['priceInCents'] as int,
lowStockThreshold: json['lowStockThreshold'] as int?,
archived: json['archived'] as bool? ?? false,
imageId: json['imageId'] as String?,
);
}
}
+51 -1
View File
@@ -13,7 +13,16 @@ enum TabEventType {
/// A stock count or tracking change made on the tablet. Not about a tab:
/// it has no `tabId`.
stockSet('stock_set');
stockSet('stock_set'),
/// A product created, changed or deleted (archived) on the tablet. Not
/// about a tab either.
productCreated('product_created'),
productUpdated('product_updated'),
productArchived('product_archived');
/// The product events, which the catalog replays instead of the tabs.
static const products = {productCreated, productUpdated, productArchived};
final String value;
@@ -177,6 +186,47 @@ class TabEventFactory {
);
}
/// A product created on this tablet: [fields] holds `name`, `category`
/// (null: none), `priceInCents` and `lowStockThreshold`. The id is the
/// tablet's, so the product can be sold before the server knows it.
TabEvent productCreated({
required String productId,
required Map<String, Object?> fields,
}) {
return _productEvent(TabEventType.productCreated, productId, fields);
}
/// Only the [fields] that changed; `imageId` is a picture uploaded first.
TabEvent productUpdated({
required String productId,
required Map<String, Object?> fields,
}) {
return _productEvent(TabEventType.productUpdated, productId, fields);
}
TabEvent productArchived({required String productId}) {
return _productEvent(TabEventType.productArchived, productId, null);
}
TabEvent _productEvent(
TabEventType type,
String productId,
Map<String, Object?>? fields,
) {
final id = _uuid.v4();
return TabEvent(
id: id,
payload: {
'id': id,
'type': type.value,
'productId': productId,
'product': ?fields,
'occurredAt': _now().toUtc().toIso8601String(),
},
);
}
/// The server's payment method names: `cash`, `payconiq`,
/// `payment_pending` or `other`.
static String paymentMethodValue(PaymentMethod method) {