payfrit-app/lib/models/order_detail.dart
John Mizerek b47c68b63a Fix type casting in order history and order detail models
Handle string/int/null values safely in JSON parsing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 21:08:39 -08:00

262 lines
7.9 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;
final List<OrderStaff> staff;
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,
required this.staff,
});
factory OrderDetail.fromJson(Map<String, dynamic> json) {
String safeStr(dynamic v) => v?.toString() ?? '';
final lineItemsJson = json['LineItems'] as List<dynamic>? ?? [];
final staffJson = json['Staff'] as List<dynamic>? ?? [];
return OrderDetail(
orderId: _parseInt(json['OrderID']) ?? 0,
businessId: _parseInt(json['BusinessID']) ?? 0,
businessName: safeStr(json['BusinessName']),
status: _parseInt(json['Status']) ?? 0,
statusText: safeStr(json['StatusText']),
orderTypeId: _parseInt(json['OrderTypeID']) ?? 0,
orderTypeName: safeStr(json['OrderTypeName']),
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: safeStr(json['Notes']),
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(),
staff: staffJson
.map((e) => OrderStaff.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: _safeInt(json['UserID']),
firstName: _safeStr(json['FirstName']),
lastName: _safeStr(json['LastName']),
phone: _safeStr(json['Phone']),
email: _safeStr(json['Email']),
);
}
static int _safeInt(dynamic v) => v is int ? v : int.tryParse(v?.toString() ?? '') ?? 0;
static String _safeStr(dynamic v) => v?.toString() ?? '';
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) {
int safeInt(dynamic v) => v is int ? v : int.tryParse(v?.toString() ?? '') ?? 0;
String safeStr(dynamic v) => v?.toString() ?? '';
return OrderServicePoint(
servicePointId: safeInt(json['ServicePointID']),
name: safeStr(json['Name']),
typeId: safeInt(json['TypeID']),
);
}
}
class OrderStaff {
final int userId;
final String firstName;
final String avatarUrl;
const OrderStaff({
required this.userId,
required this.firstName,
required this.avatarUrl,
});
factory OrderStaff.fromJson(Map<String, dynamic> json) {
int safeInt(dynamic v) => v is int ? v : int.tryParse(v?.toString() ?? '') ?? 0;
String safeStr(dynamic v) => v?.toString() ?? '';
return OrderStaff(
userId: safeInt(json['UserID']),
firstName: safeStr(json['FirstName']),
avatarUrl: safeStr(json['AvatarUrl']),
);
}
}
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) {
int safeInt(dynamic v) => v is int ? v : int.tryParse(v?.toString() ?? '') ?? 0;
double safeDouble(dynamic v) => v is num ? v.toDouble() : double.tryParse(v?.toString() ?? '') ?? 0.0;
String safeStr(dynamic v) => v?.toString() ?? '';
final modifiersJson = json['Modifiers'] as List<dynamic>? ?? [];
return OrderLineItemDetail(
lineItemId: safeInt(json['LineItemID']),
itemId: safeInt(json['ItemID']),
parentLineItemId: safeInt(json['ParentLineItemID']),
itemName: safeStr(json['ItemName']),
quantity: safeInt(json['Quantity']),
unitPrice: safeDouble(json['UnitPrice']),
remarks: safeStr(json['Remarks']),
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();
}
}