diff --git a/lib/application/void/void_form/void_form_bloc.dart b/lib/application/void/void_form/void_form_bloc.dart index 3a9169d..c7bedbb 100644 --- a/lib/application/void/void_form/void_form_bloc.dart +++ b/lib/application/void/void_form/void_form_bloc.dart @@ -85,6 +85,8 @@ class VoidFormBloc extends Bloc { orderId: originalItem.orderId, productId: originalItem.productId, productName: originalItem.productName, + categoryId: originalItem.categoryId, + categoryName: originalItem.status, productVariantId: originalItem.productVariantId, productVariantName: originalItem.productVariantName, quantity: voidQty, diff --git a/lib/domain/order/entities/order_entity.dart b/lib/domain/order/entities/order_entity.dart index 9f59f8b..5b16acc 100644 --- a/lib/domain/order/entities/order_entity.dart +++ b/lib/domain/order/entities/order_entity.dart @@ -126,6 +126,8 @@ class Order with _$Order { updatedAt: DateTime.now(), printerType: 'Barista', paidQuantity: 2, + categoryId: 'CAT-001', + categoryName: 'Minuman', ), OrderItem( id: 'ITEM-002', @@ -144,6 +146,8 @@ class Order with _$Order { updatedAt: DateTime.now(), printerType: 'Kitchen', paidQuantity: 1, + categoryId: 'CAT-002', + categoryName: 'Makanan', ), ], payments: [ @@ -207,6 +211,8 @@ class OrderItem with _$OrderItem { required DateTime updatedAt, required String printerType, required int paidQuantity, + required String categoryId, + required String categoryName, }) = _OrderItem; factory OrderItem.empty() => OrderItem( @@ -226,6 +232,8 @@ class OrderItem with _$OrderItem { updatedAt: DateTime(1970), printerType: '', paidQuantity: 0, + categoryId: '', + categoryName: '', ); factory OrderItem.fromProductQuantity(ProductQuantity productQuantity) => @@ -247,5 +255,7 @@ class OrderItem with _$OrderItem { updatedAt: DateTime.now(), printerType: productQuantity.product.printerType, paidQuantity: 0, + categoryId: '', + categoryName: '', ); } diff --git a/lib/domain/order/order.freezed.dart b/lib/domain/order/order.freezed.dart index e6adced..62847f8 100644 --- a/lib/domain/order/order.freezed.dart +++ b/lib/domain/order/order.freezed.dart @@ -1003,6 +1003,8 @@ mixin _$OrderItem { DateTime get updatedAt => throw _privateConstructorUsedError; String get printerType => throw _privateConstructorUsedError; int get paidQuantity => throw _privateConstructorUsedError; + String get categoryId => throw _privateConstructorUsedError; + String get categoryName => throw _privateConstructorUsedError; /// Create a copy of OrderItem /// with the given fields replaced by the non-null parameter values. @@ -1033,6 +1035,8 @@ abstract class $OrderItemCopyWith<$Res> { DateTime updatedAt, String printerType, int paidQuantity, + String categoryId, + String categoryName, }); } @@ -1067,6 +1071,8 @@ class _$OrderItemCopyWithImpl<$Res, $Val extends OrderItem> Object? updatedAt = null, Object? printerType = null, Object? paidQuantity = null, + Object? categoryId = null, + Object? categoryName = null, }) { return _then( _value.copyWith( @@ -1134,6 +1140,14 @@ class _$OrderItemCopyWithImpl<$Res, $Val extends OrderItem> ? _value.paidQuantity : paidQuantity // ignore: cast_nullable_to_non_nullable as int, + categoryId: null == categoryId + ? _value.categoryId + : categoryId // ignore: cast_nullable_to_non_nullable + as String, + categoryName: null == categoryName + ? _value.categoryName + : categoryName // ignore: cast_nullable_to_non_nullable + as String, ) as $Val, ); @@ -1166,6 +1180,8 @@ abstract class _$$OrderItemImplCopyWith<$Res> DateTime updatedAt, String printerType, int paidQuantity, + String categoryId, + String categoryName, }); } @@ -1199,6 +1215,8 @@ class __$$OrderItemImplCopyWithImpl<$Res> Object? updatedAt = null, Object? printerType = null, Object? paidQuantity = null, + Object? categoryId = null, + Object? categoryName = null, }) { return _then( _$OrderItemImpl( @@ -1266,6 +1284,14 @@ class __$$OrderItemImplCopyWithImpl<$Res> ? _value.paidQuantity : paidQuantity // ignore: cast_nullable_to_non_nullable as int, + categoryId: null == categoryId + ? _value.categoryId + : categoryId // ignore: cast_nullable_to_non_nullable + as String, + categoryName: null == categoryName + ? _value.categoryName + : categoryName // ignore: cast_nullable_to_non_nullable + as String, ), ); } @@ -1291,6 +1317,8 @@ class _$OrderItemImpl implements _OrderItem { required this.updatedAt, required this.printerType, required this.paidQuantity, + required this.categoryId, + required this.categoryName, }) : _modifiers = modifiers; @override @@ -1331,10 +1359,14 @@ class _$OrderItemImpl implements _OrderItem { final String printerType; @override final int paidQuantity; + @override + final String categoryId; + @override + final String categoryName; @override String toString() { - return 'OrderItem(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, paidQuantity: $paidQuantity)'; + return 'OrderItem(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, paidQuantity: $paidQuantity, categoryId: $categoryId, categoryName: $categoryName)'; } @override @@ -1371,7 +1403,11 @@ class _$OrderItemImpl implements _OrderItem { (identical(other.printerType, printerType) || other.printerType == printerType) && (identical(other.paidQuantity, paidQuantity) || - other.paidQuantity == paidQuantity)); + other.paidQuantity == paidQuantity) && + (identical(other.categoryId, categoryId) || + other.categoryId == categoryId) && + (identical(other.categoryName, categoryName) || + other.categoryName == categoryName)); } @override @@ -1393,6 +1429,8 @@ class _$OrderItemImpl implements _OrderItem { updatedAt, printerType, paidQuantity, + categoryId, + categoryName, ); /// Create a copy of OrderItem @@ -1422,6 +1460,8 @@ abstract class _OrderItem implements OrderItem { required final DateTime updatedAt, required final String printerType, required final int paidQuantity, + required final String categoryId, + required final String categoryName, }) = _$OrderItemImpl; @override @@ -1456,6 +1496,10 @@ abstract class _OrderItem implements OrderItem { String get printerType; @override int get paidQuantity; + @override + String get categoryId; + @override + String get categoryName; /// Create a copy of OrderItem /// with the given fields replaced by the non-null parameter values. diff --git a/lib/infrastructure/order/dtos/order_dto.dart b/lib/infrastructure/order/dtos/order_dto.dart index faa4a58..9bfb2d7 100644 --- a/lib/infrastructure/order/dtos/order_dto.dart +++ b/lib/infrastructure/order/dtos/order_dto.dart @@ -115,6 +115,8 @@ class OrderItemDto with _$OrderItemDto { @JsonKey(name: "updated_at") String? updatedAt, @JsonKey(name: "printer_type") String? printerType, @JsonKey(name: "paid_quantity") int? paidQuantity, + @JsonKey(name: "category_id") String? categoryId, + @JsonKey(name: "category_name") String? categoryName, }) = _OrderItemDto; factory OrderItemDto.fromJson(Map json) => @@ -138,6 +140,8 @@ class OrderItemDto with _$OrderItemDto { updatedAt: updatedAt != null ? DateTime.parse(updatedAt!) : DateTime(1970), printerType: printerType ?? '', paidQuantity: paidQuantity ?? 0, + categoryId: categoryId ?? '', + categoryName: categoryName ?? '', ); factory OrderItemDto.fromDomain(OrderItem orderItem) => OrderItemDto( @@ -157,5 +161,7 @@ class OrderItemDto with _$OrderItemDto { updatedAt: orderItem.updatedAt.toIso8601String(), printerType: orderItem.printerType, paidQuantity: orderItem.paidQuantity, + categoryId: orderItem.categoryId, + categoryName: orderItem.categoryName, ); } diff --git a/lib/infrastructure/order/order_dtos.freezed.dart b/lib/infrastructure/order/order_dtos.freezed.dart index d6b0aec..b4cab88 100644 --- a/lib/infrastructure/order/order_dtos.freezed.dart +++ b/lib/infrastructure/order/order_dtos.freezed.dart @@ -1171,6 +1171,10 @@ mixin _$OrderItemDto { String? get printerType => throw _privateConstructorUsedError; @JsonKey(name: "paid_quantity") int? get paidQuantity => throw _privateConstructorUsedError; + @JsonKey(name: "category_id") + String? get categoryId => throw _privateConstructorUsedError; + @JsonKey(name: "category_name") + String? get categoryName => throw _privateConstructorUsedError; /// Serializes this OrderItemDto to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -1206,6 +1210,8 @@ abstract class $OrderItemDtoCopyWith<$Res> { @JsonKey(name: "updated_at") String? updatedAt, @JsonKey(name: "printer_type") String? printerType, @JsonKey(name: "paid_quantity") int? paidQuantity, + @JsonKey(name: "category_id") String? categoryId, + @JsonKey(name: "category_name") String? categoryName, }); } @@ -1240,6 +1246,8 @@ class _$OrderItemDtoCopyWithImpl<$Res, $Val extends OrderItemDto> Object? updatedAt = freezed, Object? printerType = freezed, Object? paidQuantity = freezed, + Object? categoryId = freezed, + Object? categoryName = freezed, }) { return _then( _value.copyWith( @@ -1307,6 +1315,14 @@ class _$OrderItemDtoCopyWithImpl<$Res, $Val extends OrderItemDto> ? _value.paidQuantity : paidQuantity // ignore: cast_nullable_to_non_nullable as int?, + categoryId: freezed == categoryId + ? _value.categoryId + : categoryId // ignore: cast_nullable_to_non_nullable + as String?, + categoryName: freezed == categoryName + ? _value.categoryName + : categoryName // ignore: cast_nullable_to_non_nullable + as String?, ) as $Val, ); @@ -1339,6 +1355,8 @@ abstract class _$$OrderItemDtoImplCopyWith<$Res> @JsonKey(name: "updated_at") String? updatedAt, @JsonKey(name: "printer_type") String? printerType, @JsonKey(name: "paid_quantity") int? paidQuantity, + @JsonKey(name: "category_id") String? categoryId, + @JsonKey(name: "category_name") String? categoryName, }); } @@ -1372,6 +1390,8 @@ class __$$OrderItemDtoImplCopyWithImpl<$Res> Object? updatedAt = freezed, Object? printerType = freezed, Object? paidQuantity = freezed, + Object? categoryId = freezed, + Object? categoryName = freezed, }) { return _then( _$OrderItemDtoImpl( @@ -1439,6 +1459,14 @@ class __$$OrderItemDtoImplCopyWithImpl<$Res> ? _value.paidQuantity : paidQuantity // ignore: cast_nullable_to_non_nullable as int?, + categoryId: freezed == categoryId + ? _value.categoryId + : categoryId // ignore: cast_nullable_to_non_nullable + as String?, + categoryName: freezed == categoryName + ? _value.categoryName + : categoryName // ignore: cast_nullable_to_non_nullable + as String?, ), ); } @@ -1464,6 +1492,8 @@ class _$OrderItemDtoImpl extends _OrderItemDto { @JsonKey(name: "updated_at") this.updatedAt, @JsonKey(name: "printer_type") this.printerType, @JsonKey(name: "paid_quantity") this.paidQuantity, + @JsonKey(name: "category_id") this.categoryId, + @JsonKey(name: "category_name") this.categoryName, }) : _modifiers = modifiers, super._(); @@ -1526,10 +1556,16 @@ class _$OrderItemDtoImpl extends _OrderItemDto { @override @JsonKey(name: "paid_quantity") final int? paidQuantity; + @override + @JsonKey(name: "category_id") + final String? categoryId; + @override + @JsonKey(name: "category_name") + final String? categoryName; @override String toString() { - return 'OrderItemDto(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, paidQuantity: $paidQuantity)'; + return 'OrderItemDto(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, paidQuantity: $paidQuantity, categoryId: $categoryId, categoryName: $categoryName)'; } @override @@ -1566,7 +1602,11 @@ class _$OrderItemDtoImpl extends _OrderItemDto { (identical(other.printerType, printerType) || other.printerType == printerType) && (identical(other.paidQuantity, paidQuantity) || - other.paidQuantity == paidQuantity)); + other.paidQuantity == paidQuantity) && + (identical(other.categoryId, categoryId) || + other.categoryId == categoryId) && + (identical(other.categoryName, categoryName) || + other.categoryName == categoryName)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -1589,6 +1629,8 @@ class _$OrderItemDtoImpl extends _OrderItemDto { updatedAt, printerType, paidQuantity, + categoryId, + categoryName, ); /// Create a copy of OrderItemDto @@ -1623,6 +1665,8 @@ abstract class _OrderItemDto extends OrderItemDto { @JsonKey(name: "updated_at") final String? updatedAt, @JsonKey(name: "printer_type") final String? printerType, @JsonKey(name: "paid_quantity") final int? paidQuantity, + @JsonKey(name: "category_id") final String? categoryId, + @JsonKey(name: "category_name") final String? categoryName, }) = _$OrderItemDtoImpl; const _OrderItemDto._() : super._(); @@ -1677,6 +1721,12 @@ abstract class _OrderItemDto extends OrderItemDto { @override @JsonKey(name: "paid_quantity") int? get paidQuantity; + @override + @JsonKey(name: "category_id") + String? get categoryId; + @override + @JsonKey(name: "category_name") + String? get categoryName; /// Create a copy of OrderItemDto /// with the given fields replaced by the non-null parameter values. diff --git a/lib/infrastructure/order/order_dtos.g.dart b/lib/infrastructure/order/order_dtos.g.dart index 314c85f..f577a9d 100644 --- a/lib/infrastructure/order/order_dtos.g.dart +++ b/lib/infrastructure/order/order_dtos.g.dart @@ -110,6 +110,8 @@ _$OrderItemDtoImpl _$$OrderItemDtoImplFromJson(Map json) => updatedAt: json['updated_at'] as String?, printerType: json['printer_type'] as String?, paidQuantity: (json['paid_quantity'] as num?)?.toInt(), + categoryId: json['category_id'] as String?, + categoryName: json['category_name'] as String?, ); Map _$$OrderItemDtoImplToJson(_$OrderItemDtoImpl instance) => @@ -130,6 +132,8 @@ Map _$$OrderItemDtoImplToJson(_$OrderItemDtoImpl instance) => 'updated_at': instance.updatedAt, 'printer_type': instance.printerType, 'paid_quantity': instance.paidQuantity, + 'category_id': instance.categoryId, + 'category_name': instance.categoryName, }; _$OrderRequestDtoImpl _$$OrderRequestDtoImplFromJson( diff --git a/lib/presentation/components/print/print_ui.dart b/lib/presentation/components/print/print_ui.dart index 1488abf..2bf05b2 100644 --- a/lib/presentation/components/print/print_ui.dart +++ b/lib/presentation/components/print/print_ui.dart @@ -88,9 +88,9 @@ class PrintUi { paperSize: paper, ); - for (final item in order.orderItems) { - bytes += generator.reset(); + bytes += generator.reset(); + for (final item in order.orderItems) { bytes += builder.separator(); bytes += builder.printerType(printerType: 'CHECKER'); bytes += builder.separator(); @@ -118,11 +118,11 @@ class PrintUi { variantName: item.productVariantName, notes: item.notes, ); - - bytes += builder.separator(); - bytes += builder.footer(); } + bytes += builder.separator(); + bytes += builder.cutOnly(); + return bytes; } @@ -144,7 +144,17 @@ class PrintUi { paperSize: paper, ); + // Group items by category + final Map> groupedItems = {}; for (final item in order.orderItems) { + final key = item.categoryName.isNotEmpty ? item.categoryName : 'Lainnya'; + groupedItems.putIfAbsent(key, () => []).add(item); + } + + for (final entry in groupedItems.entries) { + final categoryName = entry.key; + final items = entry.value; + bytes += generator.reset(); bytes += builder.separator(); @@ -158,17 +168,20 @@ class PrintUi { tableNumber: order.tableNumber, ); - bytes += builder.separator(); + bytes += builder.orderType(categoryName); + bytes += builder.emptyLines(1); - bytes += builder.orderItem( - productName: item.productName, - quantity: item.quantity, - unitPrice: item.unitPrice.currencyFormatRpV2, - totalPrice: item.totalPrice.currencyFormatRpV2, - variantName: item.productVariantName, - notes: item.notes, - ); + for (final item in items) { + bytes += builder.orderItem( + productName: item.productName, + quantity: item.quantity, + unitPrice: item.unitPrice.currencyFormatRpV2, + totalPrice: item.totalPrice.currencyFormatRpV2, + variantName: item.productVariantName, + notes: item.notes, + ); + } bytes += builder.separator(); bytes += builder.footer(); diff --git a/lib/presentation/components/print/receipt_component_builder.dart b/lib/presentation/components/print/receipt_component_builder.dart index 5c2dd4a..bb351e7 100644 --- a/lib/presentation/components/print/receipt_component_builder.dart +++ b/lib/presentation/components/print/receipt_component_builder.dart @@ -9,11 +9,6 @@ class ReceiptComponentBuilder { ReceiptComponentBuilder({required this.generator, this.paperSize = 58}); - /// Get separator line based on paper size - String get _separator => paperSize == 80 - ? '------------------------------------------------' - : '--------------------------------'; - /// Print text centered with custom style List textCenter( String text, { @@ -48,11 +43,21 @@ class ReceiptComponentBuilder { ); } + /// Characters per line based on paper size (always size1 font) + String get _separatorLine => paperSize == 80 + ? '------------------------------------------------' + : '--------------------------------'; + /// Print separator line List separator({bool bold = false}) { return generator.text( - _separator, - styles: PosStyles(bold: bold, align: PosAlign.center), + _separatorLine, + styles: PosStyles( + bold: bold, + align: PosAlign.center, + height: PosTextSize.size1, + width: PosTextSize.size1, + ), ); } @@ -116,6 +121,10 @@ class ReceiptComponentBuilder { return generator.feed(count); } + /// Helper: returns size2 for 80mm paper, size1 for 58mm + PosTextSize get _titleSize => + paperSize == 80 ? PosTextSize.size2 : PosTextSize.size1; + /// Print header (outlet info) List header({ required String outletName, @@ -127,8 +136,8 @@ class ReceiptComponentBuilder { bytes += textCenter( outletName, bold: true, - height: PosTextSize.size1, - width: PosTextSize.size1, + height: _titleSize, + width: _titleSize, ); bytes += textCenter(address); bytes += textCenter(phoneNumber); @@ -143,8 +152,8 @@ class ReceiptComponentBuilder { bytes += textCenter( printerType, bold: true, - height: PosTextSize.size1, - width: PosTextSize.size1, + height: _titleSize, + width: _titleSize, ); return bytes; @@ -168,21 +177,16 @@ class ReceiptComponentBuilder { }) { List bytes = []; - bytes += row2Columns('Nomor', orderNumber, leftWidth: 4, rightWidth: 8); + bytes += row2Columns('Nomor', orderNumber); bytes += row2Columns('Pelanggan', customerName); - bytes += row2Columns('Kasir', cashierName, leftWidth: 4, rightWidth: 8); + bytes += row2Columns('Kasir', cashierName); if (paymentMethod != null) { - bytes += row2Columns( - 'Pembayaran', - paymentMethod, - leftWidth: 8, - rightWidth: 4, - ); + bytes += row2Columns('Pembayaran', paymentMethod); } if (tableNumber != null && tableNumber.isNotEmpty) { - bytes += row2Columns('Meja', tableNumber, leftWidth: 8, rightWidth: 4); + bytes += row2Columns('Meja', tableNumber); } return bytes; @@ -193,7 +197,7 @@ class ReceiptComponentBuilder { List bytes = []; bytes += separator(); - bytes += textCenter(type, bold: true); + bytes += textCenter(type, bold: true, height: _titleSize, width: _titleSize); bytes += separator(); return bytes; @@ -214,7 +218,7 @@ class ReceiptComponentBuilder { ? '$productName ($variantName)' : productName; - bytes += textLeft(displayName); + bytes += textLeft(displayName, bold: paperSize == 80); bytes += row2Columns( '$quantity x $unitPrice', totalPrice, @@ -263,19 +267,27 @@ class ReceiptComponentBuilder { bytes += textCenter( message, bold: true, - height: PosTextSize.size1, - width: PosTextSize.size1, + height: _titleSize, + width: _titleSize, ); if (kDebugMode) { - bytes += textCenter("$paperSize MM", ); + bytes += textCenter("$paperSize MM"); } bytes += feed(paperSize == 80 ? 3 : 1); - + bytes += generator.cut(); return bytes; } + /// Cut paper without printing footer message + List cutOnly() { + List bytes = []; + bytes += feed(paperSize == 80 ? 3 : 1); + bytes += generator.cut(); + return bytes; + } + /// Print QR Code List qrCode(String data, {PosAlign align = PosAlign.center}) { return generator.qrcode(data, align: align);