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>
This commit is contained in:
John Mizerek 2026-01-23 21:08:39 -08:00
parent 28e41a445e
commit b47c68b63a
4 changed files with 53 additions and 36 deletions

View file

@ -43,22 +43,23 @@ class OrderDetail {
});
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: (json['BusinessName'] as String?) ?? '',
businessName: safeStr(json['BusinessName']),
status: _parseInt(json['Status']) ?? 0,
statusText: (json['StatusText'] as String?) ?? '',
statusText: safeStr(json['StatusText']),
orderTypeId: _parseInt(json['OrderTypeID']) ?? 0,
orderTypeName: (json['OrderTypeName'] as String?) ?? '',
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: (json['Notes'] as String?) ?? '',
notes: safeStr(json['Notes']),
createdOn: _parseDateTime(json['CreatedOn']),
submittedOn: _parseDateTimeNullable(json['SubmittedOn']),
updatedOn: _parseDateTimeNullable(json['UpdatedOn']),
@ -135,14 +136,17 @@ class OrderCustomer {
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?) ?? '',
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(' ');
@ -161,10 +165,12 @@ class OrderServicePoint {
});
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: (json['ServicePointID'] as num?)?.toInt() ?? 0,
name: (json['Name'] as String?) ?? '',
typeId: (json['TypeID'] as num?)?.toInt() ?? 0,
servicePointId: safeInt(json['ServicePointID']),
name: safeStr(json['Name']),
typeId: safeInt(json['TypeID']),
);
}
}
@ -181,10 +187,12 @@ class OrderStaff {
});
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: (json['UserID'] as num?)?.toInt() ?? 0,
firstName: (json['FirstName'] as String?) ?? '',
avatarUrl: (json['AvatarUrl'] as String?) ?? '',
userId: safeInt(json['UserID']),
firstName: safeStr(json['FirstName']),
avatarUrl: safeStr(json['AvatarUrl']),
);
}
}
@ -213,16 +221,19 @@ class OrderLineItemDetail {
});
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: (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?) ?? '',
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>))

View file

@ -28,20 +28,24 @@ class OrderHistoryItem {
});
factory OrderHistoryItem.fromJson(Map<String, dynamic> json) {
int parseId(dynamic val) => val is int ? val : int.tryParse(val.toString()) ?? 0;
double parseDouble(dynamic val) => val is num ? val.toDouble() : double.tryParse(val.toString()) ?? 0.0;
String parseStr(dynamic val) => val?.toString() ?? "";
return OrderHistoryItem(
orderId: (json["OrderID"] as num).toInt(),
orderUuid: json["OrderUUID"] as String? ?? "",
businessId: (json["BusinessID"] as num).toInt(),
businessName: json["BusinessName"] as String? ?? "Unknown",
total: (json["OrderTotal"] as num?)?.toDouble() ?? 0.0,
statusId: (json["OrderStatusID"] as num).toInt(),
statusName: json["StatusName"] as String? ?? "Unknown",
orderTypeId: (json["OrderTypeID"] as num?)?.toInt() ?? 0,
typeName: json["TypeName"] as String? ?? "Unknown",
itemCount: (json["ItemCount"] as num?)?.toInt() ?? 0,
createdAt: DateTime.tryParse(json["CreatedAt"] as String? ?? "") ?? DateTime.now(),
completedAt: json["CompletedAt"] != null && (json["CompletedAt"] as String).isNotEmpty
? DateTime.tryParse(json["CompletedAt"] as String)
orderId: parseId(json["OrderID"]),
orderUuid: parseStr(json["OrderUUID"]),
businessId: parseId(json["BusinessID"]),
businessName: parseStr(json["BusinessName"]).isEmpty ? "Unknown" : parseStr(json["BusinessName"]),
total: parseDouble(json["OrderTotal"]),
statusId: parseId(json["OrderStatusID"]),
statusName: parseStr(json["StatusName"]).isEmpty ? "Unknown" : parseStr(json["StatusName"]),
orderTypeId: parseId(json["OrderTypeID"]),
typeName: parseStr(json["TypeName"]).isEmpty ? "Unknown" : parseStr(json["TypeName"]),
itemCount: parseId(json["ItemCount"]),
createdAt: DateTime.tryParse(parseStr(json["CreatedAt"])) ?? DateTime.now(),
completedAt: parseStr(json["CompletedAt"]).isNotEmpty
? DateTime.tryParse(parseStr(json["CompletedAt"]))
: null,
);
}

View file

@ -183,6 +183,8 @@ class _AddressListScreenState extends State<AddressListScreen> {
_error!,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 24),
FilledButton.icon(

View file

@ -561,7 +561,7 @@ class _CartViewScreenState extends State<CartViewScreen> {
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Text(_error!, textAlign: TextAlign.center),
Text(_error!, textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadCart,