dev #1

Merged
aefril merged 128 commits from dev into main 2025-08-13 17:19:48 +00:00
3 changed files with 360 additions and 289 deletions
Showing only changes of commit 2c8b1333d8 - Show all commits

View File

@ -1,35 +1,5 @@
import 'dart:convert'; import 'dart:convert';
class OrderResponseModel {
final bool? success;
final OrderData? data;
final dynamic errors;
OrderResponseModel({
this.success,
this.data,
this.errors,
});
factory OrderResponseModel.fromJson(String str) =>
OrderResponseModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory OrderResponseModel.fromMap(Map<String, dynamic> json) =>
OrderResponseModel(
success: json["success"],
data: json["data"] == null ? null : OrderData.fromMap(json["data"]),
errors: json["errors"],
);
Map<String, dynamic> toMap() => {
"success": success,
"data": data?.toMap(),
"errors": errors,
};
}
class OrderDetailResponseModel { class OrderDetailResponseModel {
final bool? success; final bool? success;
final Order? data; final Order? data;
@ -60,40 +30,77 @@ class OrderDetailResponseModel {
}; };
} }
class OrderResponseModel {
final bool? success;
final OrderData? data;
OrderResponseModel({
this.success,
this.data,
});
factory OrderResponseModel.fromJson(String str) =>
OrderResponseModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory OrderResponseModel.fromMap(Map<String, dynamic> map) {
return OrderResponseModel(
success: map['success'] ?? false,
data: OrderData.fromMap(map['data']),
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data?.toMap(),
};
}
}
class OrderData { class OrderData {
final List<Order>? orders; final List<Order>? orders;
final List<Payment>? payments;
final int? totalCount; final int? totalCount;
final int? page; final int? page;
final int? limit; final int? limit;
final int? totalPages; final int? totalPages;
OrderData({ OrderData({
this.orders, required this.orders,
this.totalCount, required this.payments,
this.page, required this.totalCount,
this.limit, required this.page,
this.totalPages, required this.limit,
required this.totalPages,
}); });
factory OrderData.fromMap(Map<String, dynamic> json) => OrderData( factory OrderData.fromMap(Map<String, dynamic> map) {
orders: json["orders"] == null return OrderData(
? [] orders: map["orders"] == null
: List<Order>.from(json["orders"].map((x) => Order.fromMap(x))), ? []
totalCount: json["total_count"], : List<Order>.from(map['orders']?.map((x) => Order.fromMap(x))),
page: json["page"], payments: map["orders"] == null
limit: json["limit"], ? []
totalPages: json["total_pages"], : List<Payment>.from(map['payments']?.map((x) => Payment.fromMap(x))),
); totalCount: map['total_count'],
page: map['page'],
limit: map['limit'],
totalPages: map['total_pages'],
);
}
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"orders": orders == null return {
? [] 'orders': orders?.map((x) => x.toMap()).toList(),
: List<dynamic>.from(orders!.map((x) => x.toMap())), 'payments': payments?.map((x) => x.toMap()).toList(),
"total_count": totalCount, 'total_count': totalCount,
"page": page, 'page': page,
"limit": limit, 'limit': limit,
"total_pages": totalPages, 'total_pages': totalPages,
}; };
}
} }
class Order { class Order {
@ -108,12 +115,20 @@ class Order {
final int? taxAmount; final int? taxAmount;
final int? discountAmount; final int? discountAmount;
final int? totalAmount; final int? totalAmount;
final int? totalCost;
final int? remainingAmount;
final String? paymentStatus;
final int? refundAmount;
final bool? isVoid;
final bool? isRefund;
final String? notes; final String? notes;
final Map<String, dynamic>? metadata; final Map<String, dynamic>? metadata;
final DateTime? createdAt; final DateTime? createdAt;
final DateTime? updatedAt; final DateTime? updatedAt;
final List<OrderItem>? orderItems; final List<OrderItem>? orderItems;
final bool? isRefund; final List<Payment>? payments;
final int? totalPaid;
final int? paymentCount;
Order({ Order({
this.id, this.id,
@ -127,79 +142,104 @@ class Order {
this.taxAmount, this.taxAmount,
this.discountAmount, this.discountAmount,
this.totalAmount, this.totalAmount,
this.totalCost,
this.remainingAmount,
this.paymentStatus,
this.refundAmount,
this.isVoid,
this.isRefund,
this.notes, this.notes,
this.metadata, this.metadata,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.orderItems, this.orderItems,
this.isRefund, this.payments,
this.totalPaid,
this.paymentCount,
}); });
factory Order.fromMap(Map<String, dynamic> json) => Order( factory Order.fromMap(Map<String, dynamic> map) {
id: json["id"], return Order(
orderNumber: json["order_number"], id: map['id'],
outletId: json["outlet_id"], orderNumber: map['order_number'],
userId: json["user_id"], outletId: map['outlet_id'],
tableNumber: json["table_number"], userId: map['user_id'],
orderType: json["order_type"], tableNumber: map['table_number'],
status: json["status"], orderType: map['order_type'],
subtotal: json["subtotal"], status: map['status'],
taxAmount: json["tax_amount"], subtotal: map['subtotal'],
discountAmount: json["discount_amount"], taxAmount: map['tax_amount'],
totalAmount: json["total_amount"], discountAmount: map['discount_amount'],
notes: json["notes"], totalAmount: map['total_amount'],
metadata: json["metadata"] ?? {}, totalCost: map['total_cost'],
isRefund: json["is_refund"], remainingAmount: map['remaining_amount'],
createdAt: json["created_at"] == null paymentStatus: map['payment_status'],
? null refundAmount: map['refund_amount'],
: DateTime.parse(json["created_at"]), isVoid: map['is_void'],
updatedAt: json["updated_at"] == null isRefund: map['is_refund'],
? null notes: map['notes'],
: DateTime.parse(json["updated_at"]), metadata: map['metadata'] ?? {},
orderItems: json["order_items"] == null createdAt: DateTime.parse(map['created_at']),
? [] updatedAt: DateTime.parse(map['updated_at']),
: List<OrderItem>.from( orderItems: map["order_items"] == null
json["order_items"].map((x) => OrderItem.fromMap(x))), ? []
); : List<OrderItem>.from(
map['order_items'].map((x) => OrderItem.fromMap(x))),
payments: map["payments"] == null
? []
: List<Payment>.from(map['payments'].map((x) => Payment.fromMap(x))),
totalPaid: map['total_paid'],
paymentCount: map['payment_count'],
);
}
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"id": id, return {
"order_number": orderNumber, 'id': id,
"outlet_id": outletId, 'order_number': orderNumber,
"user_id": userId, 'outlet_id': outletId,
"table_number": tableNumber, 'user_id': userId,
"order_type": orderType, 'table_number': tableNumber,
"status": status, 'order_type': orderType,
"subtotal": subtotal, 'status': status,
"tax_amount": taxAmount, 'subtotal': subtotal,
"discount_amount": discountAmount, 'tax_amount': taxAmount,
"total_amount": totalAmount, 'discount_amount': discountAmount,
"notes": notes, 'total_amount': totalAmount,
"metadata": metadata, 'total_cost': totalCost,
"created_at": createdAt?.toIso8601String(), 'remaining_amount': remainingAmount,
"updated_at": updatedAt?.toIso8601String(), 'payment_status': paymentStatus,
"is_refund": isRefund, 'refund_amount': refundAmount,
"order_items": orderItems == null 'is_void': isVoid,
? [] 'is_refund': isRefund,
: List<dynamic>.from(orderItems!.map((x) => x.toMap())), 'notes': notes,
}; 'metadata': metadata,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'order_items': orderItems?.map((x) => x.toMap()).toList(),
'payments': payments?.map((x) => x.toMap()).toList(),
'total_paid': totalPaid,
'payment_count': paymentCount,
};
}
} }
class OrderItem { class OrderItem {
final String? id; String? id;
final String? orderId; String? orderId;
final String? productId; String? productId;
final String? productName; String? productName;
final String? productVariantId; String? productVariantId;
final String? productVariantName; String? productVariantName;
final int? quantity; int? quantity;
final int? unitPrice; int? unitPrice;
final int? totalPrice; int? totalPrice;
final List<dynamic>? modifiers; List<dynamic>? modifiers;
final String? notes; String? notes;
final String? status; String? status;
final DateTime? createdAt; DateTime? createdAt;
final DateTime? updatedAt; DateTime? updatedAt;
String? printerType;
OrderItem({ OrderItem({
this.id, this.id,
@ -216,43 +256,119 @@ class OrderItem {
this.status, this.status,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.printerType,
}); });
factory OrderItem.fromMap(Map<String, dynamic> json) => OrderItem( factory OrderItem.fromMap(Map<String, dynamic> map) {
id: json["id"], return OrderItem(
orderId: json["order_id"], id: map['id'],
productId: json["product_id"], orderId: map['order_id'],
productName: json["product_name"], productId: map['product_id'],
productVariantId: json["product_variant_id"], productName: map['product_name'],
productVariantName: json["product_variant_name"], productVariantId: map['product_variant_id'],
quantity: json["quantity"], productVariantName: map['product_variant_name'],
unitPrice: json["unit_price"], quantity: map['quantity'],
totalPrice: json["total_price"], unitPrice: map['unit_price'],
modifiers: json["modifiers"] ?? [], totalPrice: map['total_price'],
notes: json["notes"], modifiers:
status: json["status"], map['modifiers'] == null ? [] : List<dynamic>.from(map['modifiers']),
createdAt: json["created_at"] == null notes: map['notes'],
? null status: map['status'],
: DateTime.parse(json["created_at"]), createdAt: DateTime.parse(map['created_at']),
updatedAt: json["updated_at"] == null updatedAt: DateTime.parse(map['updated_at']),
? null printerType: map['printer_type'],
: DateTime.parse(json["updated_at"]), );
); }
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() {
"id": id, return {
"order_id": orderId, 'id': id,
"product_id": productId, 'order_id': orderId,
"product_name": productName, 'product_id': productId,
"product_variant_id": productVariantId, 'product_name': productName,
"product_variant_name": productVariantName, 'product_variant_id': productVariantId,
"quantity": quantity, 'product_variant_name': productVariantName,
"unit_price": unitPrice, 'quantity': quantity,
"total_price": totalPrice, 'unit_price': unitPrice,
"modifiers": modifiers, 'total_price': totalPrice,
"notes": notes, 'modifiers': modifiers,
"status": status, 'notes': notes,
"created_at": createdAt?.toIso8601String(), 'status': status,
"updated_at": updatedAt?.toIso8601String(), 'created_at': createdAt?.toIso8601String(),
}; 'updated_at': updatedAt?.toIso8601String(),
'printer_type': printerType,
};
}
}
class Payment {
final String? id;
final String? orderId;
final String? paymentMethodId;
final String? paymentMethodName;
final String? paymentMethodType;
final int? amount;
final String? status;
final int? splitNumber;
final int? splitTotal;
final String? splitDescription;
final int? refundAmount;
final Map<String, dynamic>? metadata;
final DateTime? createdAt;
final DateTime? updatedAt;
Payment({
this.id,
this.orderId,
this.paymentMethodId,
this.paymentMethodName,
this.paymentMethodType,
this.amount,
this.status,
this.splitNumber,
this.splitTotal,
this.splitDescription,
this.refundAmount,
this.metadata,
this.createdAt,
this.updatedAt,
});
factory Payment.fromMap(Map<String, dynamic> map) {
return Payment(
id: map['id'],
orderId: map['order_id'],
paymentMethodId: map['payment_method_id'],
paymentMethodName: map['payment_method_name'],
paymentMethodType: map['payment_method_type'],
amount: map['amount'],
status: map['status'],
splitNumber: map['split_number'],
splitTotal: map['split_total'],
splitDescription: map['split_description'],
refundAmount: map['refund_amount'],
metadata: map['metadata'] ?? {},
createdAt: DateTime.parse(map['created_at']),
updatedAt: DateTime.parse(map['updated_at']),
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'order_id': orderId,
'payment_method_id': paymentMethodId,
'payment_method_name': paymentMethodName,
'payment_method_type': paymentMethodType,
'amount': amount,
'status': status,
'split_number': splitNumber,
'split_total': splitTotal,
'split_description': splitDescription,
'refund_amount': refundAmount,
'metadata': metadata,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
} }

View File

@ -63,27 +63,6 @@ class SalesListOrder extends StatelessWidget {
), ),
child: Row( child: Row(
children: [ children: [
Container(
decoration: BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isAllSelected ? AppColors.primary : Colors.grey.shade300,
width: 2,
),
),
child: Checkbox(
value: isAllSelected,
activeColor: AppColors.primary,
checkColor: AppColors.white,
onChanged: (val) {
context
.read<OrderFormBloc>()
.add(OrderFormEvent.toggleSelectAll(val ?? false));
},
),
),
const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -179,28 +158,6 @@ class SalesListOrder extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container(
decoration: BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(
color:
isSelected ? AppColors.primary : Colors.grey.shade300,
width: 1.5,
),
),
child: Checkbox(
value: isSelected,
activeColor: AppColors.primary,
checkColor: AppColors.white,
onChanged: (_) {
context
.read<OrderFormBloc>()
.add(OrderFormEvent.toggleItem(product));
},
),
),
const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -587,114 +587,112 @@ class _SplitBillPageState extends State<SplitBillPage> {
), ),
), ),
SizedBox(height: 16), SizedBox(height: 16),
Expanded( Column(
child: Column( children: [
children: [ Container(
Container( padding: EdgeInsets.all(16),
padding: EdgeInsets.all(16), decoration: BoxDecoration(
decoration: BoxDecoration( color: AppColorSplitBill.cardBackground,
color: AppColorSplitBill.cardBackground, border: Border.all(color: AppColorSplitBill.border),
border: Border.all(color: AppColorSplitBill.border), borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8), ),
), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Text(
Text( 'Split Bill',
'Split Bill', style: TextStyle(
style: TextStyle( fontSize: 16,
fontSize: 16, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, color: AppColorSplitBill.primary,
color: AppColorSplitBill.primary,
),
), ),
SizedBox(height: 16), ),
Text( SizedBox(height: 16),
'Jumlah yang akan dibayar:', Text(
style: TextStyle( 'Jumlah yang akan dibayar:',
fontSize: 14, style: TextStyle(
color: AppColorSplitBill.textSecondary, fontSize: 14,
), color: AppColorSplitBill.textSecondary,
), ),
SizedBox(height: 8), ),
Container( SizedBox(height: 8),
decoration: BoxDecoration( Container(
border: Border.all(color: AppColorSplitBill.border), decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColorSplitBill.border),
borderRadius: BorderRadius.circular(8),
),
child: TextField(
controller: amountController,
keyboardType: TextInputType.number,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColorSplitBill.textPrimary,
), ),
child: TextField( onChanged: (value) {
controller: amountController, setState(() {
keyboardType: TextInputType.number, splitAmount = int.tryParse(value) ?? 0;
style: TextStyle( });
},
decoration: InputDecoration(
hintText: '0',
prefixText: 'Rp ',
prefixStyle: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColorSplitBill.textPrimary, color: AppColorSplitBill.textPrimary,
), ),
onChanged: (value) { border: InputBorder.none,
setState(() { contentPadding: EdgeInsets.all(16),
splitAmount = int.tryParse(value) ?? 0;
});
},
decoration: InputDecoration(
hintText: '0',
prefixText: 'Rp ',
prefixStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColorSplitBill.textPrimary,
),
border: InputBorder.none,
contentPadding: EdgeInsets.all(16),
),
), ),
), ),
SizedBox(height: 16), ),
Row( SizedBox(height: 16),
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text( children: [
'Total yang dibayar:', Text(
style: TextStyle( 'Total yang dibayar:',
fontSize: 16, style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16,
color: AppColorSplitBill.textPrimary, fontWeight: FontWeight.w600,
), color: AppColorSplitBill.textPrimary,
), ),
Text( ),
'Rp ${_formatCurrency(splitAmount)}', Text(
style: TextStyle( 'Rp ${_formatCurrency(splitAmount)}',
fontSize: 16, style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16,
color: AppColorSplitBill.primary, fontWeight: FontWeight.w600,
), color: AppColorSplitBill.primary,
), ),
], ),
), ],
SizedBox(height: 8), ),
Row( SizedBox(height: 8),
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text( children: [
'Sisa bill:', Text(
style: TextStyle( 'Sisa bill:',
fontSize: 14, style: TextStyle(
color: AppColorSplitBill.textSecondary, fontSize: 14,
), color: AppColorSplitBill.textSecondary,
), ),
Text( ),
'Rp ${_formatCurrency((widget.order.totalAmount ?? 0) - splitAmount)}', Text(
style: TextStyle( 'Rp ${_formatCurrency((widget.order.totalAmount ?? 0) - splitAmount)}',
fontSize: 14, style: TextStyle(
color: AppColorSplitBill.textSecondary, fontSize: 14,
), color: AppColorSplitBill.textSecondary,
), ),
], ),
), ],
], ),
), ],
), ),
], ),
), ],
), ),
], ],
); );