Handle string/int/null values safely in JSON parsing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
262 lines
7.9 KiB
Dart
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();
|
|
}
|
|
}
|