@ -1,3 +1,5 @@
import ' dart:io ' ;
import ' package:easy_localization/easy_localization.dart ' ;
import ' package:flutter/material.dart ' ;
import ' package:flutter_staggered_animations/flutter_staggered_animations.dart ' ;
@ -20,6 +22,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import ' package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart ' ;
import ' package:hmg_patient_app_new/widgets/buttons/custom_button.dart ' ;
import ' package:hmg_patient_app_new/widgets/common_bottom_sheet.dart ' ;
import ' package:hmg_patient_app_new/widgets/image_picker.dart ' ;
import ' package:hmg_patient_app_new/widgets/input_widget.dart ' ;
import ' package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart ' ;
import ' package:hmg_patient_app_new/widgets/radio_list_tile_widget.dart ' ;
@ -240,6 +243,7 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
final paymentOptions = [
RefundTabType . hmgWallet ,
RefundTabType . invoices ,
RefundTabType . cash ,
] ;
return Column (
@ -277,14 +281,191 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
} ,
) ,
) ,
if ( vm . selectedPaymentMethod = = RefundTabType . cash & & vm . isCashRefundAmountExceeded ) . . . [
SizedBox ( height: 16. h ) ,
Container (
padding: EdgeInsets . all ( 16. h ) ,
decoration: BoxDecoration (
color: AppColors . warningColorYellow . withOpacity ( 0.1 ) ,
borderRadius: BorderRadius . circular ( 12. r ) ,
border: Border . all ( color: AppColors . warningColorYellow , width: 1 ) ,
) ,
child: Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Icon ( Icons . info_outline , color: AppColors . warningColorYellow , size: 20. h ) ,
SizedBox ( width: 12. w ) ,
Expanded (
child: LocaleKeys . cashRefundLimitExceeded . tr ( context: context ) . toText14 (
color: AppColors . textColor ,
) ,
) ,
] ,
) ,
) ,
] ,
if ( vm . selectedPaymentMethod = = RefundTabType . invoices ) . . . [
SizedBox ( height: 16. h ) ,
_buildBankInputDetails ( vm ) ,
SizedBox ( height: 16. h ) ,
_buildImageUploadSection ( vm ) ,
] ,
] ,
) ;
}
Widget _buildImageUploadSection ( RefundRequestViewModel vm ) {
return Consumer < RefundRequestViewModel > (
builder: ( context , refundVM , _ ) {
final hasError = refundVM . attachmentError ! = null ;
return Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
/ / Upload button
InkWell (
onTap: ( ) {
if ( refundVM . refundAttachmentList . length > = 4 ) {
refundVM . setAttachmentError ( LocaleKeys . maximumFilesReached . tr ( context: context ) ) ;
return ;
}
ImageOptions . showImageOptionsNew (
context ,
true ,
( String base64Image , File file ) async {
/ / Validate file size ( 2 MB max )
final fileSize = await file . length ( ) ;
if ( fileSize > RefundRequestViewModel . maxFileSizeBytes ) {
refundVM . setAttachmentError ( LocaleKeys . fileSizeTooLarge . tr ( context: context ) ) ;
return ;
}
/ / Check max files limit
if ( refundVM . refundAttachmentList . length > = RefundRequestViewModel . maxAttachments ) {
refundVM . setAttachmentError ( LocaleKeys . maximumFilesReached . tr ( context: context ) ) ;
return ;
}
/ / Add attachment
refundVM . addRefundAttachment ( base64Image ) ;
} ,
title: LocaleKeys . selectAttachment . tr ( context: context ) ,
allowMultiple: true , / / Enable multiple selection
) ;
} ,
child: Container (
padding: EdgeInsets . all ( 16. h ) ,
decoration: BoxDecoration (
color: AppColors . whiteColor ,
borderRadius: BorderRadius . circular ( 24. r ) ,
border: hasError ? Border . all ( color: AppColors . primaryRedBorderColor , width: 1 ) : null ,
) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Expanded (
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Row (
children: [
Container (
height: 40. h ,
width: 40. h ,
padding: EdgeInsets . all ( 8. h ) ,
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
borderRadius: 12. r ,
color: AppColors . greyColor ,
) ,
child: Utils . buildSvgWithAssets ( icon: AppAssets . selectImageIcon , applyThemeColor: false ) ,
) ,
SizedBox ( width: 12. w ) ,
Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
LocaleKeys . uploadImage . tr ( context: context ) . toText12 (
color: AppColors . inputLabelTextColor ,
isBold: true ,
) ,
LocaleKeys . selectImage . tr ( context: context ) . toText14 (
color: AppColors . inputLabelTextColor ,
isBold: true ,
) ,
] ,
) ,
] ,
) ,
if ( hasError ) . . . [
SizedBox ( height: 8. h ) ,
Row (
mainAxisAlignment: MainAxisAlignment . end ,
children: [
Text (
refundVM . attachmentError ! ,
style: TextStyle (
color: AppColors . primaryRedColor ,
fontSize: 12. f ,
fontWeight: FontWeight . w600 ,
) ,
) ,
] ,
) ,
] ,
] ,
) ,
) ,
Utils . buildSvgWithAssets ( icon: AppAssets . arrow_down , width: 25. h , height: 25. h ) ,
] ,
) ,
) ,
) ,
/ / Selected images list
if ( refundVM . refundAttachmentList . isNotEmpty ) . . . [
SizedBox ( height: 16. h ) ,
Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
color: AppColors . whiteColor ,
borderRadius: 24. r ,
hasShadow: false ,
) ,
child: ListView . separated (
padding: EdgeInsets . all ( 16. h ) ,
shrinkWrap: true ,
physics: const NeverScrollableScrollPhysics ( ) ,
itemCount: refundVM . refundAttachmentList . length ,
separatorBuilder: ( context , index ) = > SizedBox ( height: 12. h ) ,
itemBuilder: ( BuildContext context , int index ) {
return Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Row (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Icon (
Icons . attach_file ,
color: Color ( 0xff2B353E ) ,
size: 20. h ,
) ,
SizedBox ( width: 8. w ) ,
" ${ LocaleKeys . image . tr ( context: context ) } ${ index + 1 } " . toText14 ( ) ,
] ,
) ,
Utils . buildSvgWithAssets ( icon: AppAssets . cancel_circle_icon , width: 20. h , height: 20. h ) . onPress ( ( ) {
refundVM . removeRefundAttachment ( index ) ;
} ) ,
] ,
) ;
} ,
) ,
) ,
] ,
] ,
) ;
} ,
) ;
}
/ / ─ ─ Bank Transfer Input ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
Widget _buildBankInputDetails ( RefundRequestViewModel vm ) {
@ -522,7 +703,8 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
isDisabled: isStep2
? ( refundRequestViewModel . selectedPaymentMethod = = null | |
( refundRequestViewModel . selectedPaymentMethod = = RefundTabType . invoices & &
( _ibanController . text . trim ( ) . isEmpty | | _descriptionController . text . trim ( ) . isEmpty ) ) )
( _ibanController . text . trim ( ) . isEmpty | | _descriptionController . text . trim ( ) . isEmpty ) ) | |
( refundRequestViewModel . selectedPaymentMethod = = RefundTabType . cash & & refundRequestViewModel . isCashRefundAmountExceeded ) )
: ( refundRequestViewModel . selectedProcedures . isEmpty | |
/ / For Outpatient / ER: require refund reasons
/ / For Inpatient / Package: skip refund reason validation
@ -557,12 +739,24 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
LoaderBottomSheet . showLoader ( ) ;
final isBankTransfer = vm . selectedPaymentMethod = = RefundTabType . invoices ;
final isCash = vm . selectedPaymentMethod = = RefundTabType . cash ;
final invoice = widget . invoice ;
final userPayload = _appState . getAuthenticatedUser ( ) ;
final patientId = userPayload ! . patientId ;
final patientName = " ${ userPayload . firstName } ${ userPayload . lastName } " ;
final patientMobileNumber = " ${ userPayload . mobileNumber } " ;
final patientNationalId = userPayload . patientIdentificationNo . toString ( ) ;
/ / Determine refund destination: 1 = HMG Wallet , 2 = Bank Transfer , 3 = Cash
String refundDestination ;
if ( isCash ) {
refundDestination = ' 3 ' ;
} else if ( isBankTransfer ) {
refundDestination = ' 2 ' ;
} else {
refundDestination = ' 1 ' ; / / HMG Wallet
}
final request = SubmitRefundRequestModel (
projectId: invoice . projectId ? ? 0 ,
refundType: ' 1 ' ,
@ -579,10 +773,11 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
refundDoctorName: invoice . doctorName ? ? ' ' ,
refundClinicId: invoice . clinicId ? ? 0 ,
refundClinicName: invoice . clinicName ? ? ' ' ,
refundDestination: isBankTransfer ? ' 2 ' : ' 1 ' ,
refundDestination: refundDestination ,
refundIban: isBankTransfer ? _ibanController . text . trim ( ) : ' ' ,
refundRemarks: isBankTransfer ? _descriptionController . text . trim ( ) : ' ' ,
refundTotalAmount: vm . totalRefundableAmount ,
refundAttachmentsBase64List: vm . refundAttachmentList ,
refundProceduresList: vm . selectedProcedures
. map ( ( p ) = > SubmitRefundProcedureItem (
lineItemNo: p . apiLineItemNo ? ? 0 ,
@ -598,7 +793,7 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
request: request ,
onSuccess: ( response ) {
LoaderBottomSheet . hideLoader ( ) ;
_showSuccessBottomSheet ( response . refNo ? ? ' ' );
_showSuccessBottomSheet ( response . refNo ? ? ' ' , refundDestination );
} ,
onError: ( err ) {
LoaderBottomSheet . hideLoader ( ) ;
@ -617,7 +812,10 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
) ;
}
void _showSuccessBottomSheet ( String refNo ) {
void _showSuccessBottomSheet ( String refNo , String refundDestination ) {
/ / refundDestination: ' 1 ' = HMG Wallet , ' 2 ' = Bank Transfer , ' 3 ' = Cash
final isCashRefund = refundDestination = = ' 3 ' ;
showCommonBottomSheetWithoutHeight (
context ,
child: Padding (
@ -638,6 +836,10 @@ class _RefundRequestCreatePageState extends State<RefundRequestCreatePage> {
] ,
) . paddingSymmetrical ( 24. w , 0 ) ,
] ,
if ( isCashRefund ) . . . [
SizedBox ( height: 16. h ) ,
LocaleKeys . cashRefundApprovalInstructions . tr ( context: context ) . toText14 ( color: AppColors . textColor ) ,
] ,
SizedBox ( height: 24. h ) ,
Row (
children: [