From bd96e2684507a5aeee4ac338ea957da42a0374f6 Mon Sep 17 00:00:00 2001 From: FaizHashmi Date: Sun, 9 Aug 2026 15:28:50 +0300 Subject: [PATCH] Updated messages in refund module --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/core/api/api_client.dart | 34 +++----- lib/core/api_consts.dart | 5 +- lib/generated/locale_keys.g.dart | 2 + .../refund_request_create_page.dart | 84 ++++++++++++++++--- 6 files changed, 97 insertions(+), 40 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 8ff6d336..9233f923 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1970,8 +1970,10 @@ "refundCannotBeProcessedPatientAdmitted": "لا يمكن معالجة الاسترداد أثناء إقامة المريض", "cashRefundLimitExceeded": "لا يمكن معالجة المبالغ المستردة التي تزيد عن 5000 ريال سعودي نقداً. يرجى اختيار وجهة استرداد أخرى.", "cashRefundInstructions": "قم بزيارة صراف مستشفى الحبيب الطبي أو الحبيب للرعاية الأولية لاستلام النقد. عند نافذة الصراف، سوف تتلقى رمز التحقق على رقم هاتفك المسجل بمجرد قيام الصراف بإنشاء معاملة الاسترداد. قدم الرمز للصراف للتحقق منه. سيتم صرف النقد فقط بعد التحقق الناجح.", - "cashRefundApprovalInstructions": "قم بزيارة صراف مستشفى الحبيب الطبي أو الحبيب للرعاية الأولية لاستلام النقد. عند نافذة الصراف، سوف تتلقى رمز التحقق على رقم هاتفك المسجل بمجرد قيام الصراف بإنشاء معاملة الاسترداد. قدم الرمز للصراف للتحقق منه. سيتم صرف النقد فقط بعد التحقق الناجح.", - "packageRefundValidationMessage": "طلب استرداد الباقة الخاص بك يتطلب التحقق بسبب استخدام الباقة والفواتير الفرعية ذات الصلة. سنقوم بمراجعة ومعالجة الطلب ضمن اتفاقية مستوى الخدمة المعتمدة بعد تأكيد الأهلية.", + "cashRefundApprovalInstructions": "قم بزيارة صراف {hospital} لاستلام النقد. عند نافذة الصراف، سوف تتلقى رمز التحقق على رقم هاتفك المسجل بمجرد قيام الصراف بإنشاء معاملة الاسترداد. قدم الرمز للصراف للتحقق منه. سيتم صرف النقد فقط بعد التحقق الناجح.", + "bankTransferRefundProcessingInstructions": "سيتم تنفيذ التحويل البنكي خلال 5 أيام عمل، وذلك بعد التحقق من الأهلية.", + "packageRefundValidationMessage": "طلب استرداد الباقة الخاص بك يتطلب التحقق بسبب استخدام الباقة والفواتير الفرعية ذات الصلة. سنقوم بمراجعة ومعالجة الطلب خلال 5 أيام عمل بعد تأكيد الأهلية.", + "inpatientRefundValidationMessage": "طلب استرداد فاتورة التنويم الخاصة بك يتطلب التحقق بسبب إجراءات التنويم المتعلقة بالإجراءات أو الأدوية. سنقوم بمراجعة ومعالجة الطلب خلال 5 أيام عمل بعد تأكيد الأهلية.", "pharmacyRefundPhysicalVisitNotice": "تتطلب استردادات الصيدلية زيارة شخصية لتسليم الأدوية ومعالجة الاسترداد.", "parkingQR": "رمز الاستجابة السريعة لوقوف السيارات", "parkingQRDesc": "امسح رمز الاستجابة السريعة (QR Code) أدناه عند بوابة الخروج من موقف السيارات:", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index bd95cd4c..3098d0cb 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1960,8 +1960,10 @@ "refundCannotBeProcessedPatientAdmitted": "Refund cannot be processed while the patient is admitted", "cashRefundLimitExceeded": "Refunds over 5000 SAR cannot be processed as cash. Please select another refund destination.", "cashRefundInstructions": "Visit HMG Hospital or HMG Primary Care cashier to receive cash. At the cashier counter, you will receive an OTP on your registered mobile number once the cashier has created the refund transaction. Provide the OTP to the cashier for validation. Cash will be released only after successful validation.", - "cashRefundApprovalInstructions": "Visit HMG Hospital or HMG Primary Care cashier to receive cash. At the cashier counter, you will receive an OTP on your registered mobile number once the cashier has created the refund transaction. Provide the OTP to the cashier for validation. Cash will be released only after successful validation.", - "packageRefundValidationMessage": "Your package refund request required validation because of package use and related child invoices. We will review the process the request within the approved SLA after confirming eligibility.", + "cashRefundApprovalInstructions": "Visit {hospital} cashier to receive cash. At the cashier counter, you will receive an OTP on your registered mobile number once the cashier has created the refund transaction. Provide the OTP to the cashier for validation. Cash will be released only after successful validation.", + "bankTransferRefundProcessingInstructions": "The bank transfer will be processed within 5 working days, subject to eligibility.", + "packageRefundValidationMessage": "Your package refund request requires validation because of package use and related child invoices. We will review and process the request within 5 working days after confirming eligibility.", + "inpatientRefundValidationMessage": "Your Inpatient invoice refund request requires validation because of inpatient validations related procedure / medications. We will review and process the request within 5 working days after confirming eligibility.", "pharmacyRefundPhysicalVisitNotice": "Pharmacy refunds require a physical visit to hand over medications and process the refund.", "parkingQR": "Parking QR Code", "parkingQRDesc": "Scan the below QR Code at the exit parking gate:", diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index b1e918f6..03c880b2 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:clarity_flutter/clarity_flutter.dart'; @@ -137,8 +138,7 @@ class ApiClientImp implements ApiClient { } else {} if (body.containsKey('isDentalAllowedBackend')) { - body['isDentalAllowedBackend'] = - body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND; + body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND; } if (!body.containsKey('IsPublicRequest')) { @@ -209,11 +209,11 @@ class ApiClientImp implements ApiClient { body['TokenID'] = "@dm!n"; } - // body['TokenID'] = "@dm!n"; + body['TokenID'] = "@dm!n"; // body['PatientID'] = 6424682; // body['PatientID'] = 4772785; // body['PatientID'] = 4776766; - // body['PatientID'] = 4776462; + body['PatientID'] = 4776462; // body['PatientID'] = 4776463; // body['PatientID'] = 4776359; // body['PatientID'] = 4776466; @@ -266,7 +266,7 @@ class ApiClientImp implements ApiClient { body: requestBody, headers: headers, ); - // log("response: ${response.body.toString()}"); + log("response: ${response.body.toString()}"); } on SocketException catch (e) { final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); onFailure(message, -1, failureType: ConnectivityFailure(message)); @@ -390,24 +390,19 @@ class ApiClientImp implements ApiClient { onFailure( parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, - failureType: - UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url), + failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url), ); // logApiEndpointError(endPoint, "session logged out", statusCode); } if (isAllowAny) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else if (parsed['IsAuthenticated'] == null) { if (parsed['isSMSSent'] == true) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else if (parsed['MessageStatus'] == 1) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else if (parsed['Result'] == 'OK') { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else { onFailure( parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], @@ -417,8 +412,7 @@ class ApiClientImp implements ApiClient { logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); } } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } // else if (parsed['IsAuthenticated'] == false) { // onFailure( @@ -430,8 +424,7 @@ class ApiClientImp implements ApiClient { // } else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) { if (parsed['SameClinicApptList'] != null) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else { if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null && parsed['ErrorMessage'] == null) { if (parsed['ErrorSearchMsg'] == null) { @@ -465,8 +458,7 @@ class ApiClientImp implements ApiClient { } } else { if (parsed['SameClinicApptList'] != null) { - onSuccess(parsed, statusCode, - messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); } else { if (parsed['message'] != null) { onFailure( diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 82de820d..b6480f73 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -3,7 +3,7 @@ import 'package:flutter_amazonpaymentservices/environment_type.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { - static const maxSmallScreen = 660; + static const maxSmallScreen = 660; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; @@ -873,8 +873,7 @@ var GET_CUSTOMER_INFO = "VerifyCustomer"; //Pharmacy -var GET_PHARMACY_CATEGORISE = - 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; +var GET_PHARMACY_CATEGORISE = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; var GET_OFFERS_CATEGORISE = 'discountcategories'; var GET_OFFERS_PRODUCTS = 'offerproducts/'; var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id='; diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 06478d62..35342f84 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1952,7 +1952,9 @@ abstract class LocaleKeys { static const cashRefundLimitExceeded = 'cashRefundLimitExceeded'; static const cashRefundInstructions = 'cashRefundInstructions'; static const cashRefundApprovalInstructions = 'cashRefundApprovalInstructions'; + static const bankTransferRefundProcessingInstructions = 'bankTransferRefundProcessingInstructions'; static const packageRefundValidationMessage = 'packageRefundValidationMessage'; + static const inpatientRefundValidationMessage = 'inpatientRefundValidationMessage'; static const pharmacyRefundPhysicalVisitNotice = 'pharmacyRefundPhysicalVisitNotice'; static const parkingQR = 'parkingQR'; static const parkingQRDesc = 'parkingQRDesc'; diff --git a/lib/presentation/refund_request/refund_request_create_page.dart b/lib/presentation/refund_request/refund_request_create_page.dart index 2e8289a7..2dce1c4a 100644 --- a/lib/presentation/refund_request/refund_request_create_page.dart +++ b/lib/presentation/refund_request/refund_request_create_page.dart @@ -231,8 +231,7 @@ class _RefundRequestCreatePageState extends State { ), ), ), - if (_canSelectIndividualProcedures) - Divider(color: AppColors.bottomNAVBorder, height: 1, thickness: 1).paddingSymmetrical(16.w, 0.w), + if (_canSelectIndividualProcedures) Divider(color: AppColors.bottomNAVBorder, height: 1, thickness: 1).paddingSymmetrical(16.w, 0.w), ...List.generate( vm.procedures.length, (index) => _buildProcedureCard(vm.procedures[index], index, vm.procedures.length), @@ -255,11 +254,35 @@ class _RefundRequestCreatePageState extends State { RefundTabType.invoices, RefundTabType.cash, ]; + final hospitalName = (widget.invoice.projectName ?? '').trim(); + final isPackageInvoice = widget.invoice.isPackageInvoice == true; + final isInpatientInvoice = widget.invoice.isOutPatient == false; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.selectRefundMethod.tr(context: context).toText20(isBold: true), + if (isPackageInvoice || isInpatientInvoice) ...[ + SizedBox(height: 12.h), + Container( + padding: EdgeInsets.all(12.h), + decoration: BoxDecoration( + color: AppColors.warningColorYellow.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(12.r), + border: Border.all(color: AppColors.warningColorYellow.withValues(alpha: 0.35), width: 1), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.info_outline, color: AppColors.warningColorYellow, size: 18.h), + SizedBox(width: 10.w), + Expanded( + child: (isPackageInvoice ? LocaleKeys.packageRefundValidationMessage : LocaleKeys.inpatientRefundValidationMessage).tr(context: context).toText12(color: AppColors.textColor), + ), + ], + ), + ), + ], SizedBox(height: 24.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -275,7 +298,9 @@ class _RefundRequestCreatePageState extends State { itemBuilder: (context, index) { final type = paymentOptions[index]; final isCashOption = type == RefundTabType.cash; + final isBankTransferOption = type == RefundTabType.invoices; final isCashSelected = vm.selectedPaymentMethod == RefundTabType.cash; + final isBankTransferSelected = vm.selectedPaymentMethod == RefundTabType.invoices; return Column( children: [ @@ -302,7 +327,12 @@ class _RefundRequestCreatePageState extends State { ), SizedBox(width: 12.w), Expanded( - child: LocaleKeys.cashRefundApprovalInstructions.tr(context: context).toText12( + child: LocaleKeys.cashRefundApprovalInstructions.tr( + context: context, + namedArgs: { + 'hospital': hospitalName.isNotEmpty ? hospitalName : 'HMG Hospital', + }, + ).toText12( color: AppColors.greyInfoTextColor, ), ), @@ -310,8 +340,29 @@ class _RefundRequestCreatePageState extends State { ), ), ], - if (index != paymentOptions.length - 1) - Divider(color: AppColors.bottomNAVBorder, height: 1, thickness: 1).paddingSymmetrical(16.w, 0.w), + if (isBankTransferOption && isBankTransferSelected) ...[ + Padding( + padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.alertSquare, + height: 24.h, + width: 24.h, + iconColor: AppColors.textColor, + ), + SizedBox(width: 12.w), + Expanded( + child: LocaleKeys.bankTransferRefundProcessingInstructions.tr(context: context).toText12( + color: AppColors.greyInfoTextColor, + ), + ), + ], + ), + ), + ], + if (index != paymentOptions.length - 1) Divider(color: AppColors.bottomNAVBorder, height: 1, thickness: 1).paddingSymmetrical(16.w, 0.w), ], ); }, @@ -659,8 +710,7 @@ class _RefundRequestCreatePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.refundReason.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true), - (procedure.refundReason ?? LocaleKeys.selectRefundReason.tr(context: context)) - .toText14(color: AppColors.textColor, isBold: true), + (procedure.refundReason ?? LocaleKeys.selectRefundReason.tr(context: context)).toText14(color: AppColors.textColor, isBold: true), ], ), ), @@ -741,8 +791,7 @@ class _RefundRequestCreatePageState extends State { }, isDisabled: isStep2 ? (refundRequestViewModel.selectedPaymentMethod == null || - (refundRequestViewModel.selectedPaymentMethod == RefundTabType.invoices && - (_ibanController.text.trim().isEmpty || _descriptionController.text.trim().isEmpty)) || + (refundRequestViewModel.selectedPaymentMethod == RefundTabType.invoices && (_ibanController.text.trim().isEmpty || _descriptionController.text.trim().isEmpty)) || (refundRequestViewModel.selectedPaymentMethod == RefundTabType.cash && refundRequestViewModel.isCashRefundAmountExceeded)) : (refundRequestViewModel.selectedProcedures.isEmpty || // For Outpatient/ER: require refund reasons @@ -855,6 +904,8 @@ class _RefundRequestCreatePageState extends State { // refundDestination: '1' = HMG Wallet, '2' = Bank Transfer, '3' = Cash final isCashRefund = refundDestination == '3'; final isPackageInvoice = widget.invoice.isPackageInvoice == true; + final isInpatientInvoice = widget.invoice.isOutPatient == false; + final hospitalName = (widget.invoice.projectName ?? '').trim(); showCommonBottomSheetWithoutHeight( context, @@ -874,17 +925,26 @@ class _RefundRequestCreatePageState extends State { SizedBox(width: 6.w), refNo.toText18(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], - ).paddingSymmetrical(24.w, 0), + ), ], - if (isCashRefund || isPackageInvoice) ...[ + if (isCashRefund || isPackageInvoice || isInpatientInvoice) ...[ if (isCashRefund) ...[ SizedBox(height: 16.h), - LocaleKeys.cashRefundApprovalInstructions.tr(context: context).toText14(color: AppColors.textColor), + LocaleKeys.cashRefundApprovalInstructions.tr( + context: context, + namedArgs: { + 'hospital': hospitalName.isNotEmpty ? hospitalName : 'HMG Hospital', + }, + ).toText14(color: AppColors.textColor), ], if (isPackageInvoice) ...[ SizedBox(height: 16.h), LocaleKeys.packageRefundValidationMessage.tr(context: context).toText14(color: AppColors.textColor), ], + if (!isPackageInvoice && isInpatientInvoice) ...[ + SizedBox(height: 16.h), + LocaleKeys.inpatientRefundValidationMessage.tr(context: context).toText14(color: AppColors.textColor), + ], ], SizedBox(height: 24.h), Row(