payfrit-app/lib/models/order_detail.dart
John Mizerek 77e3145175 Add About screen and Order Detail screen with modifiers
- Add About Payfrit screen with app info, features, and contact details
- Add Order Detail screen showing line items with non-default modifiers
- Add order_detail model with parent-child line item hierarchy
- Update order history to navigate to detail screen on tap
- Add getOrderDetail API method
- Add about route to app router

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 12:41:44 -08:00

225 lines
6.7 KiB
Dart

/// Model for order detail with line items and modifiers
class OrderDetail {
final int orderId;
final int businessId;
final String businessName;
final int status;
final String statusText;
final int orderTypeId;
final String orderTypeName;
final double subtotal;
final double tax;
final double tip;
final double total;
final String notes;
final DateTime createdOn;
final DateTime? submittedOn;
final DateTime? updatedOn;
final OrderCustomer customer;
final OrderServicePoint servicePoint;
final List<OrderLineItemDetail> lineItems;
const OrderDetail({
required this.orderId,
required this.businessId,
required this.businessName,
required this.status,
required this.statusText,
required this.orderTypeId,
required this.orderTypeName,
required this.subtotal,
required this.tax,
required this.tip,
required this.total,
required this.notes,
required this.createdOn,
this.submittedOn,
this.updatedOn,
required this.customer,
required this.servicePoint,
required this.lineItems,
});
factory OrderDetail.fromJson(Map<String, dynamic> json) {
final lineItemsJson = json['LineItems'] as List<dynamic>? ?? [];
return OrderDetail(
orderId: _parseInt(json['OrderID']) ?? 0,
businessId: _parseInt(json['BusinessID']) ?? 0,
businessName: (json['BusinessName'] as String?) ?? '',
status: _parseInt(json['Status']) ?? 0,
statusText: (json['StatusText'] as String?) ?? '',
orderTypeId: _parseInt(json['OrderTypeID']) ?? 0,
orderTypeName: (json['OrderTypeName'] as String?) ?? '',
subtotal: _parseDouble(json['Subtotal']) ?? 0.0,
tax: _parseDouble(json['Tax']) ?? 0.0,
tip: _parseDouble(json['Tip']) ?? 0.0,
total: _parseDouble(json['Total']) ?? 0.0,
notes: (json['Notes'] as String?) ?? '',
createdOn: _parseDateTime(json['CreatedOn']),
submittedOn: _parseDateTimeNullable(json['SubmittedOn']),
updatedOn: _parseDateTimeNullable(json['UpdatedOn']),
customer: OrderCustomer.fromJson(json['Customer'] as Map<String, dynamic>? ?? {}),
servicePoint: OrderServicePoint.fromJson(json['ServicePoint'] as Map<String, dynamic>? ?? {}),
lineItems: lineItemsJson
.map((e) => OrderLineItemDetail.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
/// Get only non-default modifiers for display
List<OrderLineItemDetail> getNonDefaultModifiers(OrderLineItemDetail item) {
return item.modifiers.where((m) => !m.isDefault).toList();
}
static int? _parseInt(dynamic value) {
if (value == null) return null;
if (value is int) return value;
if (value is num) return value.toInt();
if (value is String && value.isNotEmpty) return int.tryParse(value);
return null;
}
static double? _parseDouble(dynamic value) {
if (value == null) return null;
if (value is double) return value;
if (value is num) return value.toDouble();
if (value is String && value.isNotEmpty) return double.tryParse(value);
return null;
}
static DateTime _parseDateTime(dynamic value) {
if (value == null) return DateTime.now();
if (value is DateTime) return value;
if (value is String && value.isNotEmpty) {
try {
return DateTime.parse(value);
} catch (_) {}
}
return DateTime.now();
}
static DateTime? _parseDateTimeNullable(dynamic value) {
if (value == null) return null;
if (value is String && value.isEmpty) return null;
if (value is DateTime) return value;
if (value is String) {
try {
return DateTime.parse(value);
} catch (_) {}
}
return null;
}
}
class OrderCustomer {
final int userId;
final String firstName;
final String lastName;
final String phone;
final String email;
const OrderCustomer({
required this.userId,
required this.firstName,
required this.lastName,
required this.phone,
required this.email,
});
factory OrderCustomer.fromJson(Map<String, dynamic> json) {
return OrderCustomer(
userId: (json['UserID'] as num?)?.toInt() ?? 0,
firstName: (json['FirstName'] as String?) ?? '',
lastName: (json['LastName'] as String?) ?? '',
phone: (json['Phone'] as String?) ?? '',
email: (json['Email'] as String?) ?? '',
);
}
String get fullName {
final parts = [firstName, lastName].where((s) => s.isNotEmpty);
return parts.isEmpty ? 'Guest' : parts.join(' ');
}
}
class OrderServicePoint {
final int servicePointId;
final String name;
final int typeId;
const OrderServicePoint({
required this.servicePointId,
required this.name,
required this.typeId,
});
factory OrderServicePoint.fromJson(Map<String, dynamic> json) {
return OrderServicePoint(
servicePointId: (json['ServicePointID'] as num?)?.toInt() ?? 0,
name: (json['Name'] as String?) ?? '',
typeId: (json['TypeID'] as num?)?.toInt() ?? 0,
);
}
}
class OrderLineItemDetail {
final int lineItemId;
final int itemId;
final int parentLineItemId;
final String itemName;
final int quantity;
final double unitPrice;
final String remarks;
final bool isDefault;
final List<OrderLineItemDetail> modifiers;
const OrderLineItemDetail({
required this.lineItemId,
required this.itemId,
required this.parentLineItemId,
required this.itemName,
required this.quantity,
required this.unitPrice,
required this.remarks,
required this.isDefault,
required this.modifiers,
});
factory OrderLineItemDetail.fromJson(Map<String, dynamic> json) {
final modifiersJson = json['Modifiers'] as List<dynamic>? ?? [];
return OrderLineItemDetail(
lineItemId: (json['LineItemID'] as num?)?.toInt() ?? 0,
itemId: (json['ItemID'] as num?)?.toInt() ?? 0,
parentLineItemId: (json['ParentLineItemID'] as num?)?.toInt() ?? 0,
itemName: (json['ItemName'] as String?) ?? '',
quantity: (json['Quantity'] as num?)?.toInt() ?? 1,
unitPrice: (json['UnitPrice'] as num?)?.toDouble() ?? 0.0,
remarks: (json['Remarks'] as String?) ?? '',
isDefault: json['IsDefault'] == true,
modifiers: modifiersJson
.map((e) => OrderLineItemDetail.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
/// Calculate total price for this item including modifiers
double get totalPrice {
double total = unitPrice * quantity;
for (final mod in modifiers) {
total += mod.unitPrice * mod.quantity;
}
return total;
}
/// Check if this item has any non-default modifiers
bool get hasNonDefaultModifiers {
return modifiers.any((m) => !m.isDefault);
}
/// Get only non-default modifiers
List<OrderLineItemDetail> get nonDefaultModifiers {
return modifiers.where((m) => !m.isDefault).toList();
}
}