feat: add alot
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/// User-facing failures. View models expose these and views translate them
|
||||
/// with `AppLocalizations.errorText`, so a typo can't silently skip l10n.
|
||||
enum AppError {
|
||||
loadBar,
|
||||
createTab,
|
||||
outOfStock,
|
||||
addProductToTab,
|
||||
updateQuantity,
|
||||
closeTab,
|
||||
renameTab,
|
||||
deleteTab,
|
||||
loadHistory,
|
||||
loadMoreTabs,
|
||||
loadProducts,
|
||||
addProduct,
|
||||
updateProduct,
|
||||
deleteProduct,
|
||||
checkPinStatus,
|
||||
savePin,
|
||||
incorrectPin,
|
||||
verifyPin,
|
||||
currentPinIncorrect,
|
||||
tooManyPinAttempts,
|
||||
loadSettings,
|
||||
saveSettings,
|
||||
}
|
||||
+4
-22
@@ -1,23 +1,20 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'tab_item.dart';
|
||||
import '../utils/collection_utils.dart';
|
||||
|
||||
class BarTab {
|
||||
final String id;
|
||||
final String customerName;
|
||||
final String status;
|
||||
final DateTime openedAt;
|
||||
final DateTime? closedAt;
|
||||
final int version;
|
||||
final List<TabItem> items;
|
||||
|
||||
const BarTab({
|
||||
required this.id,
|
||||
required this.customerName,
|
||||
required this.status,
|
||||
required this.openedAt,
|
||||
this.version = 0,
|
||||
required this.items,
|
||||
this.closedAt,
|
||||
});
|
||||
|
||||
int get totalInCents {
|
||||
@@ -28,32 +25,17 @@ class BarTab {
|
||||
return items.fold<int>(0, (total, item) => total + item.quantity);
|
||||
}
|
||||
|
||||
String get formattedTotal {
|
||||
return '€${(totalInCents / 100).toStringAsFixed(2)}';
|
||||
}
|
||||
|
||||
bool get isOpen => status == 'open';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BarTab &&
|
||||
id == other.id &&
|
||||
customerName == other.customerName &&
|
||||
status == other.status &&
|
||||
openedAt == other.openedAt &&
|
||||
closedAt == other.closedAt &&
|
||||
version == other.version &&
|
||||
listEquals(items, other.items);
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
id,
|
||||
customerName,
|
||||
status,
|
||||
openedAt,
|
||||
closedAt,
|
||||
version,
|
||||
Object.hashAll(items),
|
||||
);
|
||||
int get hashCode =>
|
||||
Object.hash(id, customerName, openedAt, version, Object.hashAll(items));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'closed_tab_item.dart';
|
||||
import 'payment_method.dart';
|
||||
import '../utils/collection_utils.dart';
|
||||
|
||||
class ClosedTab {
|
||||
final String id;
|
||||
@@ -10,8 +9,8 @@ class ClosedTab {
|
||||
final String customerName;
|
||||
final DateTime closedAt;
|
||||
final PaymentMethod paymentMethod;
|
||||
|
||||
final List<ClosedTabItem> items;
|
||||
|
||||
const ClosedTab({
|
||||
required this.id,
|
||||
required this.originalTabId,
|
||||
@@ -27,11 +26,6 @@ class ClosedTab {
|
||||
int get totalInCents =>
|
||||
items.fold(0, (sum, item) => sum + item.lineTotalInCents);
|
||||
|
||||
String get formattedTotal =>
|
||||
NumberFormat.simpleCurrency().format(totalInCents / 100);
|
||||
|
||||
String get formattedPaymentMethod => paymentMethod.label;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
enum PaymentMethod {
|
||||
cash('cash', 'Cash', 'Cash payment'),
|
||||
payconiq('payconiq', 'Payconiq', 'Payconiq QR payment'),
|
||||
paymentPending('payment_pending', 'Payment pending', 'Unpaid remote close');
|
||||
cash('cash'),
|
||||
payconiq('payconiq'),
|
||||
paymentPending('payment_pending');
|
||||
|
||||
final String value;
|
||||
final String label;
|
||||
final String description;
|
||||
|
||||
const PaymentMethod(this.value, this.label, this.description);
|
||||
const PaymentMethod(this.value);
|
||||
|
||||
static PaymentMethod fromValue(String value) => PaymentMethod.values.firstWhere(
|
||||
(e) => e.value == value,
|
||||
static PaymentMethod fromValue(String value) =>
|
||||
PaymentMethod.values.firstWhere(
|
||||
(method) => method.value == value,
|
||||
orElse: () => PaymentMethod.cash,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-15
@@ -21,12 +21,6 @@ class Product {
|
||||
|
||||
bool get isLowStock => stockQuantity <= lowStockThreshold;
|
||||
|
||||
String get formattedPrice {
|
||||
final euros = priceInCents / 100;
|
||||
|
||||
return '€${euros.toStringAsFixed(2)}';
|
||||
}
|
||||
|
||||
Product copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
@@ -64,13 +58,13 @@ class Product {
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
id,
|
||||
name,
|
||||
category,
|
||||
stockQuantity,
|
||||
lowStockThreshold,
|
||||
priceInCents,
|
||||
imagePath,
|
||||
active,
|
||||
);
|
||||
id,
|
||||
name,
|
||||
category,
|
||||
stockQuantity,
|
||||
lowStockThreshold,
|
||||
priceInCents,
|
||||
imagePath,
|
||||
active,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
import '../utils/app_config.dart';
|
||||
|
||||
enum CommandType {
|
||||
closeTab('close_tab');
|
||||
|
||||
final String value;
|
||||
|
||||
const CommandType(this.value);
|
||||
|
||||
static CommandType? fromValue(String value) {
|
||||
for (final type in CommandType.values) {
|
||||
if (type.value == value) return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
enum CommandResult { completed, conflict, failed, expired }
|
||||
|
||||
class RemoteCommand {
|
||||
final String id;
|
||||
final String type;
|
||||
final String rawType;
|
||||
final String tabId;
|
||||
final int expectedVersion;
|
||||
final String paymentMethod;
|
||||
@@ -9,7 +28,7 @@ class RemoteCommand {
|
||||
|
||||
const RemoteCommand({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.rawType,
|
||||
required this.tabId,
|
||||
required this.expectedVersion,
|
||||
required this.paymentMethod,
|
||||
@@ -20,7 +39,7 @@ class RemoteCommand {
|
||||
factory RemoteCommand.fromJson(Map<String, dynamic> json) {
|
||||
return RemoteCommand(
|
||||
id: json['id'] as String,
|
||||
type: json['type'] as String,
|
||||
rawType: json['type'] as String,
|
||||
tabId: json['tabId'] as String,
|
||||
expectedVersion: json['expectedVersion'] as int,
|
||||
paymentMethod: json['paymentMethod'] as String? ?? 'cash',
|
||||
@@ -29,8 +48,13 @@ class RemoteCommand {
|
||||
);
|
||||
}
|
||||
|
||||
/// Null when the server sends a command this app version doesn't know.
|
||||
CommandType? get type => CommandType.fromValue(rawType);
|
||||
|
||||
bool get isExpired {
|
||||
final cutoff = DateTime.now().subtract(const Duration(hours: 24));
|
||||
final cutoff = DateTime.now().subtract(
|
||||
const Duration(hours: kCommandExpiryHours),
|
||||
);
|
||||
return createdAt.isBefore(cutoff);
|
||||
}
|
||||
}
|
||||
@@ -40,24 +64,23 @@ class SyncResponse {
|
||||
final String? cursor;
|
||||
final List<String> plannedTabNames;
|
||||
|
||||
SyncResponse({
|
||||
const SyncResponse({
|
||||
required this.commands,
|
||||
this.cursor,
|
||||
this.plannedTabNames = const [],
|
||||
});
|
||||
|
||||
factory SyncResponse.fromJson(Map<String, dynamic> json) {
|
||||
final commandsList = (json['commands'] as List<dynamic>?)
|
||||
?.map((e) => RemoteCommand.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[];
|
||||
return SyncResponse(
|
||||
commands: commandsList,
|
||||
commands: [
|
||||
for (final command in json['commands'] as List<dynamic>? ?? [])
|
||||
RemoteCommand.fromJson(command as Map<String, dynamic>),
|
||||
],
|
||||
cursor: json['cursor'] as String?,
|
||||
plannedTabNames: (json['plannedTabNames'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
plannedTabNames: [
|
||||
for (final name in json['plannedTabNames'] as List<dynamic>? ?? [])
|
||||
name as String,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -78,12 +101,12 @@ class SnapshotTab {
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'customerName': customerName,
|
||||
'version': version,
|
||||
'totalInCents': totalInCents,
|
||||
'itemCount': itemCount,
|
||||
};
|
||||
'id': id,
|
||||
'customerName': customerName,
|
||||
'version': version,
|
||||
'totalInCents': totalInCents,
|
||||
'itemCount': itemCount,
|
||||
};
|
||||
}
|
||||
|
||||
class SyncRequest {
|
||||
@@ -98,15 +121,15 @@ class SyncRequest {
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'deviceId': deviceId,
|
||||
if (cursor != null) 'cursor': cursor,
|
||||
'openTabs': openTabs.map((t) => t.toJson()).toList(),
|
||||
};
|
||||
'deviceId': deviceId,
|
||||
if (cursor != null) 'cursor': cursor,
|
||||
'openTabs': openTabs.map((tab) => tab.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
class CommandAck {
|
||||
final String commandId;
|
||||
final String status;
|
||||
final CommandResult status;
|
||||
final String? result;
|
||||
|
||||
const CommandAck({
|
||||
@@ -116,10 +139,10 @@ class CommandAck {
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'commandId': commandId,
|
||||
'status': status,
|
||||
if (result != null) 'result': result,
|
||||
};
|
||||
'commandId': commandId,
|
||||
'status': status.name,
|
||||
if (result != null) 'result': result,
|
||||
};
|
||||
}
|
||||
|
||||
enum RemoteSyncEventType {
|
||||
@@ -127,6 +150,7 @@ enum RemoteSyncEventType {
|
||||
syncCompleted,
|
||||
syncFailed,
|
||||
conflict,
|
||||
unauthorized,
|
||||
}
|
||||
|
||||
class RemoteSyncEvent {
|
||||
@@ -141,4 +165,4 @@ class RemoteSyncEvent {
|
||||
this.requestedBy,
|
||||
this.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'tab_item_purchase.dart';
|
||||
import '../utils/collection_utils.dart';
|
||||
|
||||
class TabItem {
|
||||
final String id;
|
||||
@@ -22,14 +23,6 @@ class TabItem {
|
||||
|
||||
int get lineTotalInCents => quantity * unitPriceInCents;
|
||||
|
||||
String get formattedLineTotal {
|
||||
return '€${(lineTotalInCents / 100).toStringAsFixed(2)}';
|
||||
}
|
||||
|
||||
String get formattedUnitPrice {
|
||||
return '€${(unitPriceInCents / 100).toStringAsFixed(2)}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
|
||||
@@ -4,8 +4,6 @@ class Venue {
|
||||
|
||||
const Venue({required this.id, required this.name});
|
||||
|
||||
factory Venue.fromJson(Map<String, dynamic> json) => Venue(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
}
|
||||
factory Venue.fromJson(Map<String, dynamic> json) =>
|
||||
Venue(id: json['id'] as String, name: json['name'] as String);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user