first commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
|
||||
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../setting/bloc/discount/discount_bloc.dart';
|
||||
|
||||
class DiscountDialog extends StatefulWidget {
|
||||
const DiscountDialog({super.key});
|
||||
|
||||
@override
|
||||
State<DiscountDialog> createState() => _DiscountDialogState();
|
||||
}
|
||||
|
||||
class _DiscountDialogState extends State<DiscountDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<DiscountBloc>().add(const DiscountEvent.getDiscounts());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
int discountIdSelected = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'DISKON',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.cancel,
|
||||
color: AppColors.primary,
|
||||
size: 30.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: BlocBuilder<DiscountBloc, DiscountState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
orElse: () => const SizedBox.shrink(),
|
||||
loading: () => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
loaded: (discounts) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: discounts
|
||||
.map(
|
||||
(discount) => ListTile(
|
||||
title: Text('Nama Diskon: ${discount.name}'),
|
||||
subtitle: Text('Potongan harga (${discount.value}%)'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
textColor: AppColors.primary,
|
||||
trailing: Checkbox(
|
||||
value: discount.id == discountIdSelected,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
discountIdSelected = discount.id!;
|
||||
context.read<CheckoutBloc>().add(
|
||||
CheckoutEvent.addDiscount(
|
||||
discount,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
// context.pop();
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
|
||||
import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart';
|
||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
import 'package:widgets_to_image/widgets_to_image.dart';
|
||||
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
||||
|
||||
import '../../../core/components/spaces.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../bloc/order/order_bloc.dart';
|
||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
||||
|
||||
class PaymentQrisDialog extends StatefulWidget {
|
||||
final List<ProductQuantity> items;
|
||||
final int discount;
|
||||
final int discountAmount;
|
||||
final int tax;
|
||||
final int serviceCharge;
|
||||
final int paymentAmount;
|
||||
final String customerName;
|
||||
final int tableNumber;
|
||||
final String status;
|
||||
final String paymentStatus;
|
||||
final String paymentMethod;
|
||||
final int price;
|
||||
final int totalQty;
|
||||
final int subTotal;
|
||||
final bool? isTablePaymentPage;
|
||||
const PaymentQrisDialog({
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.discount,
|
||||
required this.discountAmount,
|
||||
required this.tax,
|
||||
required this.serviceCharge,
|
||||
required this.paymentAmount,
|
||||
required this.customerName,
|
||||
required this.tableNumber,
|
||||
required this.status,
|
||||
required this.paymentStatus,
|
||||
required this.paymentMethod,
|
||||
required this.price,
|
||||
required this.totalQty,
|
||||
required this.subTotal,
|
||||
this.isTablePaymentPage = false,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PaymentQrisDialog> createState() => _PaymentQrisDialogState();
|
||||
}
|
||||
|
||||
class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
|
||||
String orderId = '';
|
||||
Timer? timer;
|
||||
|
||||
WidgetsToImageController controller = WidgetsToImageController();
|
||||
@override
|
||||
void initState() {
|
||||
orderId = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
context.read<QrisBloc>().add(QrisEvent.generateQRCode(
|
||||
orderId,
|
||||
widget.price,
|
||||
));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
backgroundColor: AppColors.primary,
|
||||
content: SizedBox(
|
||||
width: context.deviceWidth * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: Text(
|
||||
'Pembayaran QRIS',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontFamily: 'Quicksand',
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SpaceHeight(6.0),
|
||||
Container(
|
||||
width: context.deviceWidth,
|
||||
padding: const EdgeInsets.all(14.0),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
||||
color: AppColors.white,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BlocListener<QrisBloc, QrisState>(
|
||||
listener: (context, state) {
|
||||
state.maybeWhen(orElse: () {
|
||||
return;
|
||||
}, qrisResponse: (data) {
|
||||
const onSec = Duration(seconds: 5);
|
||||
timer = Timer.periodic(onSec, (timer) {
|
||||
context
|
||||
.read<QrisBloc>()
|
||||
.add(QrisEvent.checkPaymentStatus(
|
||||
orderId.toString(),
|
||||
));
|
||||
});
|
||||
}, success: (message) async {
|
||||
context.read<OrderBloc>().add(OrderEvent.order(
|
||||
widget.items,
|
||||
widget.discount,
|
||||
widget.discountAmount,
|
||||
widget.tax,
|
||||
widget.serviceCharge,
|
||||
widget.paymentAmount,
|
||||
widget.customerName,
|
||||
widget.tableNumber,
|
||||
'completed',
|
||||
'paid',
|
||||
'Qris',
|
||||
widget.price,
|
||||
OrderType.dineIn));
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => SuccessPaymentDialog(
|
||||
isTablePaymentPage: widget.isTablePaymentPage,
|
||||
data: widget.items,
|
||||
totalQty: widget.totalQty,
|
||||
totalPrice: widget.price,
|
||||
totalTax: widget.tax,
|
||||
totalDiscount: widget.discountAmount,
|
||||
subTotal: widget.subTotal,
|
||||
normalPrice: widget.price,
|
||||
totalService: widget.serviceCharge,
|
||||
draftName: widget.customerName,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
child: BlocBuilder<QrisBloc, QrisState>(
|
||||
builder: (context, state) {
|
||||
log('state: $state');
|
||||
return state.maybeWhen(
|
||||
orElse: () {
|
||||
return const SizedBox();
|
||||
},
|
||||
qrisResponse: (data) {
|
||||
log("URL: ${data.actions!.first.url!}");
|
||||
return WidgetsToImage(
|
||||
controller: controller,
|
||||
child: Container(
|
||||
width: 340.0,
|
||||
// height: 256.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Image.network(
|
||||
data.actions!.first.url!,
|
||||
),
|
||||
),
|
||||
// const SpaceHeight(5.0),
|
||||
Text(
|
||||
NumberFormat.currency(
|
||||
locale: 'id',
|
||||
symbol: 'Rp ',
|
||||
).format(widget.price),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return Container(
|
||||
width: 256.0,
|
||||
height: 256.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SpaceHeight(12.0),
|
||||
const Text(
|
||||
'Scan QRIS to make payment',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SpaceHeight(12.0),
|
||||
//button print qr
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final sizeReceipt = await AuthLocalDataSource().getSizeReceipt();
|
||||
final bytes = await controller.capture();
|
||||
final printValue = await PrintDataoutputs.instance
|
||||
.printQRIS(widget.price, bytes!, int.parse(sizeReceipt));
|
||||
|
||||
// Get the receipt printer to print QRIS
|
||||
final receiptPrinter = await ProductLocalDatasource.instance
|
||||
.getPrinterByCode('receipt');
|
||||
|
||||
if (receiptPrinter != null) {
|
||||
await PrinterService().printWithPrinter(
|
||||
receiptPrinter,
|
||||
printValue,
|
||||
context
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('No receipt printer configured')),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error printing QRIS: $e");
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error printing QRIS: $e')),
|
||||
);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Print QRIS',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
|
||||
|
||||
import '../../../core/constants/colors.dart';
|
||||
|
||||
class ServiceDialog extends StatelessWidget {
|
||||
const ServiceDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'LAYANAN',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () => context.pop(),
|
||||
icon: const Icon(
|
||||
Icons.cancel,
|
||||
color: AppColors.primary,
|
||||
size: 30.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BlocBuilder<CheckoutBloc, CheckoutState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
initial: () => const SizedBox(),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
loaded: (data, a, b, c, d, service, e, f, g, orderType) => ListTile(
|
||||
title: Text('Presentase ($service%)'),
|
||||
subtitle: const Text('Biaya layanan'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
textColor: AppColors.primary,
|
||||
trailing: Checkbox(
|
||||
value: service > 0,
|
||||
onChanged: (value) {
|
||||
context.read<CheckoutBloc>().add(
|
||||
CheckoutEvent.addServiceCharge(service > 0 ? 0 : service),
|
||||
);
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
orElse: () => const SizedBox(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../bloc/checkout/checkout_bloc.dart';
|
||||
|
||||
class TaxDialog extends StatelessWidget {
|
||||
const TaxDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'PAJAK PB1',
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () => context.pop(),
|
||||
icon: const Icon(
|
||||
Icons.cancel,
|
||||
color: AppColors.primary,
|
||||
size: 30.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BlocBuilder<CheckoutBloc, CheckoutState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
initial: () => const SizedBox(),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
loaded: (data, a, b, c, tax, d, e, f, g, orderType) => ListTile(
|
||||
title: const Text('PB1'),
|
||||
subtitle: Text('tarif pajak ($tax%)'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
textColor: AppColors.primary,
|
||||
trailing: Checkbox(
|
||||
value: tax > 0,
|
||||
onChanged: (value) {
|
||||
context.read<CheckoutBloc>().add(
|
||||
CheckoutEvent.addTax(tax > 0 ? 0 : tax),
|
||||
);
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
orElse: () => const SizedBox(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user