From b041cc2fb49119820018c235fe381a6d67b4d61c Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Wed, 7 Jan 2026 18:51:05 +0530 Subject: [PATCH 01/11] RCF-1354: Implemented the Maximum number of days without running the sync job and geo-location validation Signed-off-by: sachin.sp --- .../api_services/RegistrationApi.java | 3 + .../clientmanager/config/AppModule.java | 21 +- .../constant/RegistrationConstants.java | 9 + .../service/RegistrationServiceImpl.java | 108 +--- .../SyncStatusValidatorServiceImpl.java | 319 +++++++++++ .../spi/SyncStatusValidatorService.java | 26 + .../SyncStatusValidatorServiceImplTest.java | 541 ++++++++++++++++++ assets/l10n/app_ar.arb | 2 +- assets/l10n/app_en.arb | 2 +- assets/l10n/app_fr.arb | 2 +- assets/l10n/app_hi.arb | 2 +- assets/l10n/app_kn.arb | 2 +- assets/l10n/app_ta.arb | 2 +- 13 files changed, 945 insertions(+), 94 deletions(-) create mode 100644 android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java create mode 100644 android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java create mode 100644 android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java index 5fe49881f..74e3f3533 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java @@ -24,6 +24,7 @@ import io.mosip.registration.clientmanager.constant.Components; import io.mosip.registration.clientmanager.dto.registration.RegistrationDto; import io.mosip.registration.clientmanager.dto.uispec.FieldSpecDto; +import io.mosip.registration.clientmanager.exception.ClientCheckedException; import io.mosip.registration.clientmanager.service.TemplateService; import io.mosip.registration.clientmanager.spi.AuditManagerService; import io.mosip.registration.clientmanager.spi.RegistrationService; @@ -52,6 +53,8 @@ public void startRegistration(@NonNull List languages, @NonNull String f String response = ""; try { this.registrationDto = registrationService.startRegistration(languages, flowType, process); + } catch (ClientCheckedException e) { + response = e.getErrorCode(); } catch (Exception e) { response = e.getMessage(); Log.e(getClass().getSimpleName(), "Registration start failed", e); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java index 6a05a0ff6..2eeda6308 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java @@ -48,6 +48,7 @@ import io.mosip.registration.clientmanager.service.PreRegistrationDataSyncDaoImpl; import io.mosip.registration.clientmanager.service.PreRegistrationDataSyncServiceImpl; import io.mosip.registration.clientmanager.service.RegistrationServiceImpl; +import io.mosip.registration.clientmanager.service.SyncStatusValidatorServiceImpl; import io.mosip.registration.clientmanager.service.TemplateService; import io.mosip.registration.clientmanager.service.UserOnboardService; import io.mosip.registration.clientmanager.service.external.PreRegZipHandlingService; @@ -61,6 +62,7 @@ import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import io.mosip.registration.clientmanager.spi.RegistrationService; import io.mosip.registration.clientmanager.spi.SyncRestService; +import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; import io.mosip.registration.clientmanager.util.DateUtil; import io.mosip.registration.clientmanager.util.SyncRestUtil; import io.mosip.registration.clientmanager.util.UserInterfaceHelperService; @@ -206,10 +208,11 @@ RegistrationService provideRegistrationService(PacketWriterService packetWriterS LocationValidationService locationValidationService, Provider preRegistrationDataSyncServiceProvider, Biometrics095Service biometricService, - PacketService packetService) { + PacketService packetService, + SyncStatusValidatorService syncStatusValidatorService) { return new RegistrationServiceImpl(appContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService, registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService); + keyStoreRepository, globalParamRepository, auditManagerService, registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, syncStatusValidatorService); } @Provides @@ -309,4 +312,18 @@ PreRegZipHandlingService PreRegZipHandlingService(ApplicantValidDocumentDao appl PreRegistrationList PreRegistrationList() { return new PreRegistrationList(); } + + @Provides + @Singleton + SyncStatusValidatorService provideSyncStatusValidatorService(SyncJobDefRepository syncJobDefRepository, + GlobalParamRepository globalParamRepository, + JobManagerService jobManagerService, + JobTransactionService jobTransactionService, + LocationValidationService locationValidationService, + MasterDataService masterDataService, + RegistrationCenterRepository registrationCenterRepository) { + return new SyncStatusValidatorServiceImpl(appContext, syncJobDefRepository, globalParamRepository, + jobManagerService, jobTransactionService, locationValidationService, masterDataService, + registrationCenterRepository); + } } \ No newline at end of file diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java index b9009e81d..1c9195b8c 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/constant/RegistrationConstants.java @@ -118,6 +118,9 @@ public class RegistrationConstants { public static final String JOB_TRIGGER_POINT_USER = "User"; public static final String GPS_DEVICE_ENABLE_FLAG = "mosip.registration.gps_device_enable_flag"; public static final String DIST_FRM_MACHINE_TO_CENTER = "mosip.registration.distance.from.machine.to.center"; + public static final String GEO_CAP_FREQ = "mosip.registration.geo.capture.frequency"; + public static final String OPT_TO_REG_OUTSIDE_LOCATION = "OPT_TO_REG_OUTSIDE_LOCATION"; + public static final String OPT_TO_REG_LAST_CAPTURED_TIME = "OPT_TO_REG_LAST_CAPTURED_TIME"; public static final String OPERATOR_ONBOARDING_BIO_ATTRIBUTES = "mosip.registration.operator.onboarding.bioattributes"; public static final String SERVER_ACTIVE_PROFILE = "mosip.registration.server_profile"; public static final String ONBOARD_YOURSELF_URL = "mosip.registration.onboard_yourself_url"; @@ -136,4 +139,10 @@ public class RegistrationConstants { public static final String REG_PAK_MAX_CNT_OFFLINE_FREQ = "mosip.registration.packet.maximum.count.offline.frequency"; public static final String INVALID_LOGIN_COUNT = "mosip.registration.invalid_login_count"; public static final String INVALID_LOGIN_TIME = "mosip.registration.invalid_login_time"; + + // Sync status validation constants + public static final String MOSIP_REGISTRATION = "mosip.registration."; + public static final String DOT = "."; + public static final String FREQUENCY = "frequency"; + public static final String OPT_TO_REG_TIME_SYNC_EXCEED = "OPT_TO_REG_TIME_SYNC_EXCEED"; } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index 76a6af277..7fe7e34ac 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -68,6 +68,7 @@ import io.mosip.registration.clientmanager.spi.MasterDataService; import io.mosip.registration.clientmanager.spi.RegistrationService; import io.mosip.registration.clientmanager.spi.PacketService; +import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; import io.mosip.registration.clientmanager.entity.PreRegistrationList; import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import javax.inject.Provider; @@ -114,6 +115,7 @@ public class RegistrationServiceImpl implements RegistrationService { private LocationValidationService locationValidationService; private Provider preRegistrationDataSyncServiceProvider; private PacketService packetService; + private SyncStatusValidatorService syncStatusValidatorService; public static final String BOOLEAN_FALSE = "false"; private Biometrics095Service biometricService; @@ -131,7 +133,8 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter LocationValidationService locationValidationService, Provider preRegistrationDataSyncServiceProvider, Biometrics095Service biometricService, - PacketService packetService) { + PacketService packetService, + SyncStatusValidatorService syncStatusValidatorService) { this.context = context; this.registrationDto = null; this.packetWriterService = packetWriterService; @@ -147,6 +150,7 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter this.preRegistrationDataSyncServiceProvider = preRegistrationDataSyncServiceProvider; this.biometricService = biometricService; this.packetService = packetService; + this.syncStatusValidatorService = syncStatusValidatorService; } @Override @@ -193,6 +197,18 @@ public RegistrationDto startRegistration(@NonNull List languages, String } this.registrationDto = new RegistrationDto(rid, flowType, process, version, languages, bioThresholds, rid); + // Validate machine distance from registration center + if (syncStatusValidatorService != null && this.registrationDto.getGeoLocationDto() != null) { + try { + syncStatusValidatorService.validateCenterToMachineDistance( + this.registrationDto.getGeoLocationDto().getLongitude(), + this.registrationDto.getGeoLocationDto().getLatitude()); + } catch (ClientCheckedException e) { + Log.e(TAG, "Location validation failed", e); + throw e; + } + } + SharedPreferences.Editor editor = this.context.getSharedPreferences(this.context.getString(R.string.app_name), Context.MODE_PRIVATE).edit(); editor.putString(SessionManager.RID, this.registrationDto.getRId()); @@ -215,9 +231,6 @@ public void submitRegistrationDto(String makerName) throws Exception { throw new ClientCheckedException(context, R.string.err_004); } - // Validate location before submission - validateLocation(); - if (this.registrationDto.getAdditionalInfoRequestId() != null) { String newAppId = this.registrationDto.getAdditionalInfoRequestId().split("-")[0]; this.registrationDto.setApplicationId(newAppId); @@ -517,88 +530,6 @@ private List> getLabelValueDTOListString(Map return labelValueMap; } - /** - * Validate machine location against registration center - * @throws Exception if location is outside allowed distance - */ - private void validateLocation() throws Exception { - try { - - String enableFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); - boolean gpsValidationDisabled = "Y".equalsIgnoreCase(enableFlag); - if (gpsValidationDisabled) { - Log.w(TAG, "GPS distance validation disabled by config, skipping"); - return; - } - - GeoLocationDto geoLocation = this.registrationDto.getGeoLocationDto(); - if (geoLocation == null) { - Log.w(TAG, "Geo location not available, skipping validation"); - return; - } - - // Get center coordinates - CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); - if (centerMachineDto == null) { - Log.w(TAG, "Center details not found, skipping distance validation"); - return; - } - - List centers = registrationCenterRepository.getRegistrationCenter( - centerMachineDto.getCenterId()); - - if (centers == null || centers.isEmpty()) { - Log.w(TAG, "Center not found, skipping distance validation"); - return; - } - - RegistrationCenter center = centers.get(0); - String centerLatStr = center.getLatitude(); - String centerLonStr = center.getLongitude(); - - if (centerLatStr == null || centerLonStr == null || - centerLatStr.isEmpty() || centerLonStr.isEmpty()) { - Log.e(TAG, "Center coordinates not available"); - throw new ClientCheckedException(context, R.string.err_004); - } - - try { - double centerLatitude = Double.parseDouble(centerLatStr); - double centerLongitude = Double.parseDouble(centerLonStr); - - // Calculate distance - double distance = locationValidationService.getDistance( - geoLocation.getLongitude(), geoLocation.getLatitude(), - centerLongitude, centerLatitude); - - // Get max allowed distance from config - String maxDistanceStr = globalParamRepository.getCachedStringMachineToCenterDistance(); - if (maxDistanceStr == null || maxDistanceStr.isEmpty()) { - Log.e(TAG, "Max allowed distance configuration not found"); - throw new ClientCheckedException(context, R.string.err_004); - } - - double maxAllowedDistance = Double.parseDouble(maxDistanceStr); - - // Validate distance - if (distance > maxAllowedDistance) { - Log.e(TAG, "Distance not matched with allowed range"); - throw new ClientCheckedException(context, R.string.err_004); - } - - Log.i(TAG, "Location validated successfully"); - - } catch (NumberFormatException e) { - Log.e(TAG, "Invalid center coordinates format", e); - // Continue with submission even if coordinates are invalid - } - } catch (ClientCheckedException e) { - throw e; - } catch (Exception e) { - Log.e(TAG, "Location validation failed: " + e.getMessage(), e); - // Continue with submission even if validation fails - } - } public List> getAudits() { List> audits = new ArrayList<>(); @@ -641,6 +572,11 @@ private void doPreChecksBeforeRegistration(CenterMachineDto centerMachineDto) th if (centerMachineDto == null || !centerMachineDto.getCenterStatus() || !centerMachineDto.getMachineStatus()) throw new ClientCheckedException(context, R.string.err_007); + // validate sync status - checks if all sync jobs ran within configured time limits + if (syncStatusValidatorService != null) { + syncStatusValidatorService.validateSyncStatus(); + } + // registered packet approval time breach check if (packetService != null && packetService.isRegisteredPacketApprovalTimeBreached()) { throw new ClientCheckedException("PAK_APPRVL_MAX_TIME"); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java new file mode 100644 index 000000000..6ab8d781e --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -0,0 +1,319 @@ +package io.mosip.registration.clientmanager.service; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.mosip.registration.clientmanager.R; +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.entity.SyncJobDef; +import io.mosip.registration.clientmanager.exception.ClientCheckedException; +import io.mosip.registration.clientmanager.dto.CenterMachineDto; +import io.mosip.registration.clientmanager.entity.RegistrationCenter; +import io.mosip.registration.clientmanager.repository.GlobalParamRepository; +import io.mosip.registration.clientmanager.repository.RegistrationCenterRepository; +import io.mosip.registration.clientmanager.repository.SyncJobDefRepository; +import io.mosip.registration.clientmanager.spi.JobManagerService; +import io.mosip.registration.clientmanager.spi.JobTransactionService; +import io.mosip.registration.clientmanager.spi.LocationValidationService; +import io.mosip.registration.clientmanager.spi.MasterDataService; +import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; + +/** + * Validates sync status before registration. + * + * @author Sachin S P + */ +@Singleton +public class SyncStatusValidatorServiceImpl implements SyncStatusValidatorService { + + private static final String TAG = SyncStatusValidatorServiceImpl.class.getSimpleName(); + + private Context context; + private SyncJobDefRepository syncJobDefRepository; + private GlobalParamRepository globalParamRepository; + private JobManagerService jobManagerService; + private JobTransactionService jobTransactionService; + private LocationValidationService locationValidationService; + private MasterDataService masterDataService; + private RegistrationCenterRepository registrationCenterRepository; + + @Inject + public SyncStatusValidatorServiceImpl( + Context context, + SyncJobDefRepository syncJobDefRepository, + GlobalParamRepository globalParamRepository, + JobManagerService jobManagerService, + JobTransactionService jobTransactionService, + LocationValidationService locationValidationService, + MasterDataService masterDataService, + RegistrationCenterRepository registrationCenterRepository) { + this.context = context; + this.syncJobDefRepository = syncJobDefRepository; + this.globalParamRepository = globalParamRepository; + this.jobManagerService = jobManagerService; + this.jobTransactionService = jobTransactionService; + this.locationValidationService = locationValidationService; + this.masterDataService = masterDataService; + this.registrationCenterRepository = registrationCenterRepository; + } + + /** + * Validates sync job frequencies. + * + * @throws ClientCheckedException if validation fails + */ + @Override + public void validateSyncStatus() throws Exception { + Log.i(TAG, "Validating sync status started"); + + try { + // Validate sync job frequencies + validatingSyncJobsConfig(); + + Log.i(TAG, "Sync status validation completed successfully"); + + } catch (ClientCheckedException e) { + Log.e(TAG, "Sync status validation failed", e); + throw e; + } catch (Exception e) { + Log.e(TAG, "Unexpected error during sync status validation", e); + throw new ClientCheckedException(context, R.string.err_004); + } + } + + /** + * Validates sync jobs are within frequency limits. + * Only checks jobs with sync history. + * + * @throws ClientCheckedException if any job is overdue + */ + private void validatingSyncJobsConfig() throws Exception { + Log.i(TAG, "Validating sync jobs configuration started"); + + List activeJobs = syncJobDefRepository.getActiveSyncJobs(); + if (activeJobs == null || activeJobs.isEmpty()) { + Log.w(TAG, "No active sync jobs found, skipping validation"); + return; + } + + Map jobFrequencyMap = getSyncJobFrequencies(activeJobs); + if (jobFrequencyMap.isEmpty()) { + Log.w(TAG, "No sync job frequencies configured, skipping validation"); + return; + } + int syncFailureCount = 0; + StringBuilder errorDetails = new StringBuilder(); + + for (SyncJobDef syncJobDef : activeJobs) { + String jobId = syncJobDef.getId(); + String apiName = syncJobDef.getApiName(); + + if (jobId == null || apiName == null) { + Log.w(TAG, "Skipping job with null id or apiName"); + continue; + } + + String configuredFrequencyStr = jobFrequencyMap.get(jobId); + if (configuredFrequencyStr == null || configuredFrequencyStr.trim().isEmpty()) { + Log.d(TAG, "No frequency configured for job: " + jobId + " (" + apiName + ")"); + continue; + } + + try { + int configuredFrequency = Integer.parseInt(configuredFrequencyStr.trim()); + int serviceJobId = jobManagerService.generateJobServiceId(jobId); + long lastSyncTimeMillis = jobTransactionService.getLastSyncTime(serviceJobId); + + if (lastSyncTimeMillis == 0) { + Log.d(TAG, "No sync history for job: " + jobId + " (" + apiName + + ") - Skipping validation"); + continue; + } + + Date lastSyncDate = new Date(lastSyncTimeMillis); + int actualDays = getActualDays(lastSyncDate); + + Log.d(TAG, String.format( + "Job [%s] (%s): Configured frequency=%d days, Actual days since last sync=%d", + jobId, apiName, configuredFrequency, actualDays)); + if (configuredFrequency <= actualDays) { + syncFailureCount++; + errorDetails.append("- ").append(apiName) + .append(": Last sync was ").append(actualDays) + .append(" days ago (limit: ").append(configuredFrequency).append(" days)\n"); + + Log.w(TAG, String.format( + "Sync job [%s] (%s) is overdue. Configured: %d days, Actual: %d days since last sync", + jobId, apiName, configuredFrequency, actualDays)); + } + + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid frequency value for job: " + jobId + " (" + apiName + "): " + configuredFrequencyStr, e); + } catch (Exception e) { + Log.e(TAG, "Error validating job: " + jobId + " (" + apiName + ")", e); + } + } + if (syncFailureCount > 0) { + String errorMessage = "Registration blocked: " + syncFailureCount + + " sync job(s) are overdue:\n" + errorDetails.toString(); + Log.e(TAG, errorMessage); + throw new ClientCheckedException(RegistrationConstants.OPT_TO_REG_TIME_SYNC_EXCEED, errorMessage); + } + + Log.i(TAG, "All sync jobs validated successfully"); + } + + /** + * Gets sync job frequencies from config. + * + * @param activeJobs List of active sync jobs + * @return Map of jobId to frequency value + */ + private Map getSyncJobFrequencies(List activeJobs) { + Log.i(TAG, "Fetching sync job frequencies started"); + + Map jobsMap = new HashMap<>(); + + for (SyncJobDef syncJobDef : activeJobs) { + String jobId = syncJobDef.getId(); + String apiName = syncJobDef.getApiName(); + + if (apiName == null || apiName.trim().isEmpty()) { + Log.w(TAG, "Skipping job with null or empty apiName: " + jobId); + continue; + } + + String propertyName = RegistrationConstants.MOSIP_REGISTRATION + + apiName + + RegistrationConstants.DOT + + RegistrationConstants.FREQUENCY; + + String configuredValue = globalParamRepository.getCachedStringGlobalParam(propertyName); + + if (configuredValue != null && !configuredValue.trim().isEmpty() + && !configuredValue.equalsIgnoreCase("null")) { + jobsMap.put(jobId, configuredValue.trim()); + Log.d(TAG, String.format( + "Loaded frequency for job [%s] (%s): %s days", + jobId, apiName, configuredValue.trim())); + } else { + Log.d(TAG, "Frequency property not found for job [" + jobId + "] (" + apiName + "): " + propertyName); + } + } + + Log.i(TAG, "Fetched " + jobsMap.size() + " sync job frequency configurations"); + return jobsMap; + } + + /** + * Calculates days since last sync. + * + * @param lastSyncDate Last sync date + * @return Days since last sync + */ + private int getActualDays(Date lastSyncDate) { + if (lastSyncDate == null) { + return 0; + } + + long millisecondsDifference = new Date().getTime() - lastSyncDate.getTime(); + long daysDifference = millisecondsDifference / (24 * 60 * 60 * 1000); + return (int) daysDifference + 1; + } + + /** + * Validates machine distance from registration center. + * + * @param machineLongitude Machine longitude + * @param machineLatitude Machine latitude + * @throws ClientCheckedException if machine is outside allowed distance + */ + @Override + public void validateCenterToMachineDistance(Double machineLongitude, Double machineLatitude) throws Exception { + Log.i(TAG, "Validating center to machine distance started"); + + String enableFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); + if (enableFlag == null || "Y".equalsIgnoreCase(enableFlag)) { + Log.d(TAG, "GPS distance validation disabled or not configured, skipping"); + return; + } + + if (machineLongitude == null || machineLatitude == null) { + Log.d(TAG, "Machine GPS location not available, skipping validation"); + return; + } + + CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); + if (centerMachineDto == null) { + Log.w(TAG, "Center details not found, skipping distance validation"); + return; + } + + List centers = registrationCenterRepository.getRegistrationCenter( + centerMachineDto.getCenterId()); + + if (centers == null || centers.isEmpty()) { + Log.w(TAG, "Center not found, skipping distance validation"); + return; + } + + RegistrationCenter center = centers.get(0); + String centerLatStr = center.getLatitude(); + String centerLonStr = center.getLongitude(); + + if (centerLatStr == null || centerLonStr == null || + centerLatStr.isEmpty() || centerLonStr.isEmpty()) { + Log.e(TAG, "Center coordinates not available"); + throw new ClientCheckedException(context, R.string.err_004); + } + + try { + double centerLatitude = Double.parseDouble(centerLatStr); + double centerLongitude = Double.parseDouble(centerLonStr); + + double distanceKm = locationValidationService.getDistance( + machineLongitude, machineLatitude, + centerLongitude, centerLatitude); + + double distanceMeters = distanceKm * 1000; + + String maxDistanceStr = globalParamRepository.getCachedStringMachineToCenterDistance(); + if (maxDistanceStr == null || maxDistanceStr.isEmpty()) { + Log.e(TAG, "Max allowed distance configuration not found"); + throw new ClientCheckedException(context, R.string.err_004); + } + + double maxAllowedDistance = Double.parseDouble(maxDistanceStr); + + Log.d(TAG, String.format( + "Distance validation: Calculated=%.2f meters, Max allowed=%.2f meters", + distanceMeters, maxAllowedDistance)); + + if (distanceMeters > maxAllowedDistance) { + Log.e(TAG, String.format( + "Machine is outside allowed distance: %.2f meters (limit: %.2f meters)", + distanceMeters, maxAllowedDistance)); + throw new ClientCheckedException( + RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, + "Your client machine location is outside the registration center. Please note that registration can be done only from within the registration centre"); + } + + Log.i(TAG, "Location validated successfully - machine is within allowed distance"); + + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid center coordinates format", e); + throw new ClientCheckedException(context, R.string.err_004); + } + } + +} + diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java new file mode 100644 index 000000000..3acf64e92 --- /dev/null +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java @@ -0,0 +1,26 @@ +package io.mosip.registration.clientmanager.spi; + +/** + * Validates sync status before registration. + * + * @author Sachin S P + */ +public interface SyncStatusValidatorService { + + /** + * Validates sync job frequencies. + * + * @throws Exception if validation fails + */ + void validateSyncStatus() throws Exception; + + /** + * Validates machine distance from registration center using GPS. + * + * @param machineLongitude Machine longitude + * @param machineLatitude Machine latitude + * @throws Exception if machine is outside allowed distance + */ + void validateCenterToMachineDistance(Double machineLongitude, Double machineLatitude) throws Exception; +} + diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java new file mode 100644 index 000000000..4e0fe1d26 --- /dev/null +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java @@ -0,0 +1,541 @@ +package io.mosip.registration.clientmanager.service; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import android.content.Context; +import android.content.SharedPreferences; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.mosip.registration.clientmanager.R; +import io.mosip.registration.clientmanager.constant.RegistrationConstants; +import io.mosip.registration.clientmanager.dto.CenterMachineDto; +import io.mosip.registration.clientmanager.entity.RegistrationCenter; +import io.mosip.registration.clientmanager.entity.SyncJobDef; +import io.mosip.registration.clientmanager.exception.ClientCheckedException; +import io.mosip.registration.clientmanager.repository.GlobalParamRepository; +import io.mosip.registration.clientmanager.repository.RegistrationCenterRepository; +import io.mosip.registration.clientmanager.repository.SyncJobDefRepository; +import io.mosip.registration.clientmanager.spi.JobManagerService; +import io.mosip.registration.clientmanager.spi.JobTransactionService; +import io.mosip.registration.clientmanager.spi.LocationValidationService; +import io.mosip.registration.clientmanager.spi.MasterDataService; + +/** + * Unit tests for SyncStatusValidatorServiceImpl. + * + * @author Sachin S P + */ +@RunWith(MockitoJUnitRunner.class) +public class SyncStatusValidatorServiceImplTest { + + @Mock + private Context mockContext; + + @Mock + private SyncJobDefRepository mockSyncJobDefRepository; + + @Mock + private GlobalParamRepository mockGlobalParamRepository; + + @Mock + private JobManagerService mockJobManagerService; + + @Mock + private JobTransactionService mockJobTransactionService; + + @Mock + private LocationValidationService mockLocationValidationService; + + @Mock + private MasterDataService mockMasterDataService; + + @Mock + private RegistrationCenterRepository mockRegistrationCenterRepository; + + @Mock + private SharedPreferences mockSharedPreferences; + + @Mock + private SharedPreferences.Editor mockEditor; + + @InjectMocks + private SyncStatusValidatorServiceImpl syncStatusValidatorService; + + private static final String JOB_ID_1 = "MDS_J00001"; + private static final String JOB_ID_2 = "PDS_J00003"; + private static final String API_NAME_1 = "masterSyncJob"; + private static final String API_NAME_2 = "preRegistrationDataSyncJob"; + private static final String CENTER_ID = "CENTER_001"; + private static final int SERVICE_JOB_ID_1 = 1; + private static final int SERVICE_JOB_ID_2 = 3; + + @Before + public void setUp() { + MockitoAnnotations.openMocks(this); + when(mockContext.getString(R.string.app_name)).thenReturn("RegistrationClient"); + when(mockContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mockSharedPreferences); + when(mockSharedPreferences.edit()).thenReturn(mockEditor); + when(mockEditor.putLong(anyString(), anyLong())).thenReturn(mockEditor); + } + + // ========== validateSyncStatus() Tests ========== + + @Test + public void testValidateSyncStatus_Success_AllJobsWithinLimit() throws Exception { + // Setup: Create active jobs + List activeJobs = createActiveJobs(); + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: Configure frequencies + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("190"); + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_2 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("190"); + + // Setup: Last sync times (within limit - 1 day ago) + when(mockJobManagerService.generateJobServiceId(JOB_ID_1)).thenReturn(SERVICE_JOB_ID_1); + when(mockJobManagerService.generateJobServiceId(JOB_ID_2)).thenReturn(SERVICE_JOB_ID_2); + + long oneDayAgo = System.currentTimeMillis() - (24 * 60 * 60 * 1000L); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(oneDayAgo); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_2)).thenReturn(oneDayAgo); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception thrown + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateSyncStatus_Failure_JobOverdue() throws Exception { + // Setup: Create active jobs + List activeJobs = createActiveJobs(); + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: Configure frequency (1 day limit) + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("1"); + + // Setup: Last sync time (2 days ago - overdue) + when(mockJobManagerService.generateJobServiceId(JOB_ID_1)).thenReturn(SERVICE_JOB_ID_1); + long twoDaysAgo = System.currentTimeMillis() - (2 * 24 * 60 * 60 * 1000L); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(twoDaysAgo); + + // Execute - should throw exception + syncStatusValidatorService.validateSyncStatus(); + } + + @Test + public void testValidateSyncStatus_NoActiveJobs() throws Exception { + // Setup: No active jobs + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(new ArrayList<>()); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception, validation skipped + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test + public void testValidateSyncStatus_JobWithoutSyncHistory_Skipped() throws Exception { + // Setup: Create active jobs + List activeJobs = createActiveJobs(); + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: Configure frequency + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("190"); + + // Setup: No sync history (returns 0) + when(mockJobManagerService.generateJobServiceId(JOB_ID_1)).thenReturn(SERVICE_JOB_ID_1); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(0L); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception - job without history is skipped + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test + public void testValidateSyncStatus_NoFrequencyConfigured_Skipped() throws Exception { + // Setup: Create active jobs + List activeJobs = createActiveJobs(); + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: No frequency configured (returns null) + when(mockGlobalParamRepository.getCachedStringGlobalParam(anyString())).thenReturn(null); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception - job without frequency is skipped + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateSyncStatus_MultipleJobsOverdue() throws Exception { + // Setup: Create multiple active jobs + List activeJobs = new ArrayList<>(); + SyncJobDef job1 = new SyncJobDef(); + job1.setId(JOB_ID_1); + job1.setApiName(API_NAME_1); + job1.setIsActive(true); + + SyncJobDef job2 = new SyncJobDef(); + job2.setId(JOB_ID_2); + job2.setApiName(API_NAME_2); + job2.setIsActive(true); + + activeJobs.add(job1); + activeJobs.add(job2); + + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: Configure frequencies (1 day limit) + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("1"); + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_2 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("1"); + + // Setup: Both jobs overdue (2 days ago) + when(mockJobManagerService.generateJobServiceId(JOB_ID_1)).thenReturn(SERVICE_JOB_ID_1); + when(mockJobManagerService.generateJobServiceId(JOB_ID_2)).thenReturn(SERVICE_JOB_ID_2); + + long twoDaysAgo = System.currentTimeMillis() - (2 * 24 * 60 * 60 * 1000L); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(twoDaysAgo); + when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_2)).thenReturn(twoDaysAgo); + + // Execute - should throw exception + try { + syncStatusValidatorService.validateSyncStatus(); + } catch (ClientCheckedException e) { + // Verify error code + assertEquals(RegistrationConstants.OPT_TO_REG_TIME_SYNC_EXCEED, e.getErrorCode()); + assertTrue(e.getMessage().contains("2 sync job(s) are overdue")); + throw e; + } + } + + // ========== validateCenterToMachineDistance() Tests ========== + + @Test + public void testValidateCenterToMachineDistance_GPSDisabled_Skipped() throws Exception { + // Setup: GPS validation disabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("Y"); + + // Execute + syncStatusValidatorService.validateCenterToMachineDistance(77.5946, 12.9716); + + // Verify: Validation skipped + verify(mockGlobalParamRepository).getCachedStringGpsDeviceEnableFlag(); + verify(mockLocationValidationService, never()).getDistance(anyDouble(), anyDouble(), anyDouble(), anyDouble()); + } + + @Test + public void testValidateCenterToMachineDistance_LocationNull_Skipped() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Execute with null location + syncStatusValidatorService.validateCenterToMachineDistance(null, null); + + // Verify: Validation skipped + verify(mockLocationValidationService, never()).getDistance(anyDouble(), anyDouble(), anyDouble(), anyDouble()); + } + + @Test + public void testValidateCenterToMachineDistance_WithinDistance_Success() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setId(CENTER_ID); + center.setLangCode("eng"); + center.setLatitude("12.9716"); + center.setLongitude("77.5946"); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Setup: Distance calculation (within limit - 100 meters) + when(mockLocationValidationService.getDistance(78.5946, 13.9716, 77.5946, 12.9716)) + .thenReturn(0.1); // 0.1 km = 100 meters + + // Setup: Max allowed distance (500 meters) + when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn("500"); + + // Execute + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + + // Verify: No exception thrown + verify(mockLocationValidationService).getDistance(78.5946, 13.9716, 77.5946, 12.9716); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_OutsideDistance_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setId(CENTER_ID); + center.setLangCode("eng"); + center.setLatitude("12.9716"); + center.setLongitude("77.5946"); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Setup: Distance calculation (outside limit - 1000 meters) + when(mockLocationValidationService.getDistance(78.5946, 13.9716, 77.5946, 12.9716)) + .thenReturn(1.0); // 1.0 km = 1000 meters + + // Setup: Max allowed distance (500 meters) + when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn("500"); + + // Execute - should throw exception + try { + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + } catch (ClientCheckedException e) { + // Verify error code + assertEquals(RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, e.getErrorCode()); + throw e; + } + } + + @Test + public void testValidateCenterToMachineDistance_CenterDetailsNotFound_Skipped() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details not found + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(null); + + // Execute + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + + // Verify: Validation skipped + verify(mockLocationValidationService, never()).getDistance(anyDouble(), anyDouble(), anyDouble(), anyDouble()); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_CenterCoordinatesMissing_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setLatitude(null); // Missing coordinates + center.setLongitude(null); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Execute - should throw exception + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_MaxDistanceConfigMissing_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setId(CENTER_ID); + center.setLangCode("eng"); + center.setLatitude("12.9716"); + center.setLongitude("77.5946"); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Setup: Distance calculation + when(mockLocationValidationService.getDistance(78.5946, 13.9716, 77.5946, 12.9716)) + .thenReturn(0.1); + + // Setup: Max distance config missing + when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn(null); + + // Execute - should throw exception + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + } + + @Test + public void testValidateCenterToMachineDistance_InvalidCoordinateFormat_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setId(CENTER_ID); + center.setLangCode("eng"); + center.setLatitude("invalid"); // Invalid format + center.setLongitude("77.5946"); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Execute - should throw exception + try { + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + fail("Expected ClientCheckedException for invalid coordinate format"); + } catch (ClientCheckedException e) { + // Expected + } + } + + @Test + public void testValidateSyncStatus_JobWithNullId_Skipped() throws Exception { + // Setup: Create job with null ID + List activeJobs = new ArrayList<>(); + SyncJobDef job = new SyncJobDef(); + job.setId(null); + job.setApiName(API_NAME_1); + job.setIsActive(true); + activeJobs.add(job); + + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception, job skipped + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test + public void testValidateSyncStatus_JobWithNullApiName_Skipped() throws Exception { + // Setup: Create job with null apiName + List activeJobs = new ArrayList<>(); + SyncJobDef job = new SyncJobDef(); + job.setId(JOB_ID_1); + job.setApiName(null); + job.setIsActive(true); + activeJobs.add(job); + + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception, job skipped + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test + public void testValidateSyncStatus_InvalidFrequencyValue_ContinuesWithOtherJobs() throws Exception { + // Setup: Create active jobs + List activeJobs = createActiveJobs(); + when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); + + // Setup: Invalid frequency value (non-numeric) + when(mockGlobalParamRepository.getCachedStringGlobalParam( + RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) + .thenReturn("invalid"); + + // Execute + syncStatusValidatorService.validateSyncStatus(); + + // Verify: No exception, continues with other jobs + verify(mockSyncJobDefRepository).getActiveSyncJobs(); + } + + @Test + public void testValidateCenterToMachineDistance_ExactDistanceLimit_Passes() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + + // Setup: Center details + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + + RegistrationCenter center = new RegistrationCenter(); + center.setId(CENTER_ID); + center.setLangCode("eng"); + center.setLatitude("12.9716"); + center.setLongitude("77.5946"); + List centers = new ArrayList<>(); + centers.add(center); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); + + // Setup: Distance exactly at limit (500 meters) + when(mockLocationValidationService.getDistance(78.5946, 13.9716, 77.5946, 12.9716)) + .thenReturn(0.5); // 0.5 km = 500 meters + + // Setup: Max allowed distance (500 meters) + when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn("500"); + + // Execute - should pass (distance == limit, not > limit) + syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + + // Verify: No exception thrown + verify(mockLocationValidationService).getDistance(78.5946, 13.9716, 77.5946, 12.9716); + } + + // ========== Helper Methods ========== + + private List createActiveJobs() { + List jobs = new ArrayList<>(); + + SyncJobDef job1 = new SyncJobDef(); + job1.setId(JOB_ID_1); + job1.setApiName(API_NAME_1); + job1.setIsActive(true); + jobs.add(job1); + + SyncJobDef job2 = new SyncJobDef(); + job2.setId(JOB_ID_2); + job2.setApiName(API_NAME_2); + job2.setIsActive(true); + jobs.add(job2); + + return jobs; + } +} + diff --git a/assets/l10n/app_ar.arb b/assets/l10n/app_ar.arb index cee73533e..6a918cd92 100644 --- a/assets/l10n/app_ar.arb +++ b/assets/l10n/app_ar.arb @@ -302,7 +302,7 @@ "logout_success": "لقد تم تسجيل بنجاح!", "logout_failure": "حدث خطأ ما، يرجى المحاولة مرة أخرى بعد مرور بعض الوقت", "go_to_home": "اذهب إلى المنزل", - "errors": "{messages, select, REG_TRY_AGAIN{فشل تسجيل الدخول. حاول مرة أخرى!} REG_INVALID_REQUEST{كلمة المرور غير صحيحة!} REG_MACHINE_NOT_FOUND{لم يتم تسجيل هذا الجهاز بعد. يرجى التواصل مع المسؤول للحصول على المساعدة!} REG_NETWORK_ERROR{فشل تسجيل الدخول. تحقق من اتصال الشبكة!} REG_CRED_EXPIRED{لم يتم العثور على بيانات الاعتماد أو انتهت صلاحيتها. يرجى محاولة تسجيل الدخول عبر الإنترنت!} REG_MACHINE_INACTIVE{الآلة غير نشطة!} REG_CENTER_INACTIVE{المركز غير نشط!} REG_LOGIN_LOCKED{تم الوصول إلى الحد الأقصى لمحاولة تسجيل الدخول. حاول مرة أخرى لاحقا!} KER_SYN_AUTH_001{تعذر الحصول على رمز المصادقة!} PAK_APPRVL_MAX_TIME{الإجراء المطلوب: لقد تجاوزت الحزم المسجلة المعلقة وقت الموافقة المسموح به. يرجى مسح التراكم للمتابعة.} PAK_UPLOAD_MAX_TIME{الإجراء المطلوب: تحميل الحزم أو تصديرها إلى الخادم قبل متابعة التسجيل.} PAK_UPLOAD_MAX_COUNT{الإجراء المطلوب: تم الوصول إلى الحد الأقصى للحزمة. الرجاء تصدير أو تحميل الحزم الموجودة قبل إنشاء تسجيلات جديدة.} other{'Some error occurred!'}}", + "errors": "{messages, select, REG_TRY_AGAIN{فشل تسجيل الدخول. حاول مرة أخرى!} REG_INVALID_REQUEST{كلمة المرور غير صحيحة!} REG_MACHINE_NOT_FOUND{لم يتم تسجيل هذا الجهاز بعد. يرجى التواصل مع المسؤول للحصول على المساعدة!} REG_NETWORK_ERROR{فشل تسجيل الدخول. تحقق من اتصال الشبكة!} REG_CRED_EXPIRED{لم يتم العثور على بيانات الاعتماد أو انتهت صلاحيتها. يرجى محاولة تسجيل الدخول عبر الإنترنت!} REG_MACHINE_INACTIVE{الآلة غير نشطة!} REG_CENTER_INACTIVE{المركز غير نشط!} REG_LOGIN_LOCKED{تم الوصول إلى الحد الأقصى لمحاولة تسجيل الدخول. حاول مرة أخرى لاحقا!} KER_SYN_AUTH_001{تعذر الحصول على رمز المصادقة!} PAK_APPRVL_MAX_TIME{الإجراء المطلوب: لقد تجاوزت الحزم المسجلة المعلقة وقت الموافقة المسموح به. يرجى مسح التراكم للمتابعة.} PAK_UPLOAD_MAX_TIME{الإجراء المطلوب: تحميل الحزم أو تصديرها إلى الخادم قبل متابعة التسجيل.} PAK_UPLOAD_MAX_COUNT{الإجراء المطلوب: تم الوصول إلى الحد الأقصى للحزمة. الرجاء تصدير أو تحميل الحزم الموجودة قبل إنشاء تسجيلات جديدة.} OPT_TO_REG_TIME_SYNC_EXCEED{تجاوز الوقت منذ آخر مزامنة الحد الأقصى المسموح به. يرجى المزامنة من الخادم قبل المتابعة مع هذا التسجيل.} OPT_TO_REG_OUTSIDE_LOCATION{موقع جهاز العميل الخاص بك خارج مركز التسجيل. يرجى ملاحظة أنه يمكن إجراء التسجيل فقط من داخل مركز التسجيل} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 40a829e60..899acc846 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -302,7 +302,7 @@ "logout_success": "You have been successfully logged out!", "logout_failure": "Something went wrong, please try again after some time", "go_to_home": "Go To Home", - "errors": "{messages, select, REG_TRY_AGAIN{Login Failed. Try Again!} REG_INVALID_REQUEST{Password incorrect!} REG_MACHINE_NOT_FOUND{This device has not been onboarded yet. Please reach out to your administrator for assistance!} REG_NETWORK_ERROR{Login failed. Check network connection!} REG_CRED_EXPIRED{Credentials not found or are expired. Please try online login!} REG_MACHINE_INACTIVE{Machine is not active!} REG_CENTER_INACTIVE{Center is not active!} REG_LOGIN_LOCKED{Maximum login attempt limit reached. Try again later!} KER_SYN_AUTH_001{Unable to get Authentication Token!} PAK_APPRVL_MAX_TIME{Action required: Pending registered packets have exceeded the allowed approval time. Please clear the backlog to continue.} PAK_UPLOAD_MAX_TIME{Action required: Upload or export the packets to the server before proceeding with registration.} PAK_UPLOAD_MAX_COUNT{Action required: Packet limit reached. Please export or upload existing packets before creating new registrations.} other{'Some error occurred!'}}", + "errors": "{messages, select, REG_TRY_AGAIN{Login Failed. Try Again!} REG_INVALID_REQUEST{Password incorrect!} REG_MACHINE_NOT_FOUND{This device has not been onboarded yet. Please reach out to your administrator for assistance!} REG_NETWORK_ERROR{Login failed. Check network connection!} REG_CRED_EXPIRED{Credentials not found or are expired. Please try online login!} REG_MACHINE_INACTIVE{Machine is not active!} REG_CENTER_INACTIVE{Center is not active!} REG_LOGIN_LOCKED{Maximum login attempt limit reached. Try again later!} KER_SYN_AUTH_001{Unable to get Authentication Token!} PAK_APPRVL_MAX_TIME{Action required: Pending registered packets have exceeded the allowed approval time. Please clear the backlog to continue.} PAK_UPLOAD_MAX_TIME{Action required: Upload or export the packets to the server before proceeding with registration.} PAK_UPLOAD_MAX_COUNT{Action required: Packet limit reached. Please export or upload existing packets before creating new registrations.} OPT_TO_REG_TIME_SYNC_EXCEED{Time since last sync exceeded maximum limit. Please sync from server before proceeding with this registration.} OPT_TO_REG_OUTSIDE_LOCATION{Your client machine location is outside the registration center. Please note that registration can be done only from within the registration centre} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { diff --git a/assets/l10n/app_fr.arb b/assets/l10n/app_fr.arb index e828d66c0..8dfba7778 100644 --- a/assets/l10n/app_fr.arb +++ b/assets/l10n/app_fr.arb @@ -302,7 +302,7 @@ "logout_success": "Vous avez été déconnecté avec succès!", "logout_failure": "Quelque chose s'est mal passé, veuillez réessayer après un certain temps", "go_to_home": "Aller à la maison", - "errors": "{messages, select, REG_TRY_AGAIN{Echec de la connexion.. Réessayez!} REG_INVALID_REQUEST{Mot de passe incorrect!} REG_MACHINE_NOT_FOUND{Cet appareil n’a pas encore été intégré. Veuillez contacter votre administrateur pour obtenir de l’aide !} REG_NETWORK_ERROR{Échec de la connexion. Vérifiez la connexion réseau!} REG_CRED_EXPIRED{Les informations d’identification sont introuvables ou ont expiré. S’il vous plaît essayer la connexion en ligne!} REG_MACHINE_INACTIVE{La machine n'est pas active!} REG_CENTER_INACTIVE{Le centre n'est pas actif!} REG_LOGIN_LOCKED{Limite maximale de tentatives de connexion atteinte. Réessayez plus tard!} KER_SYN_AUTH_001{Impossible d’obtenir le jeton d’authentification!} PAK_APPRVL_MAX_TIME{Action requise : les paquets enregistrés en attente ont dépassé le délai d'approbation autorisé. Veuillez éliminer l'arriéré pour continuer.} PAK_UPLOAD_MAX_TIME{Action requise : Téléchargez ou exportez les paquets vers le serveur avant de procéder à l'enregistrement.} PAK_UPLOAD_MAX_COUNT{Action requise : limite de paquets atteinte. Veuillez exporter ou télécharger les paquets existants avant de créer de nouvelles inscriptions.} other{'Some error occurred!'}}", + "errors": "{messages, select, REG_TRY_AGAIN{Echec de la connexion.. Réessayez!} REG_INVALID_REQUEST{Mot de passe incorrect!} REG_MACHINE_NOT_FOUND{Cet appareil n'a pas encore été intégré. Veuillez contacter votre administrateur pour obtenir de l'aide !} REG_NETWORK_ERROR{Échec de la connexion. Vérifiez la connexion réseau!} REG_CRED_EXPIRED{Les informations d'identification sont introuvables ou ont expiré. S'il vous plaît essayer la connexion en ligne!} REG_MACHINE_INACTIVE{La machine n'est pas active!} REG_CENTER_INACTIVE{Le centre n'est pas actif!} REG_LOGIN_LOCKED{Limite maximale de tentatives de connexion atteinte. Réessayez plus tard!} KER_SYN_AUTH_001{Impossible d'obtenir le jeton d'authentification!} PAK_APPRVL_MAX_TIME{Action requise : les paquets enregistrés en attente ont dépassé le délai d'approbation autorisé. Veuillez éliminer l'arriéré pour continuer.} PAK_UPLOAD_MAX_TIME{Action requise : Téléchargez ou exportez les paquets vers le serveur avant de procéder à l'enregistrement.} PAK_UPLOAD_MAX_COUNT{Action requise : limite de paquets atteinte. Veuillez exporter ou télécharger les paquets existants avant de créer de nouvelles inscriptions.} OPT_TO_REG_TIME_SYNC_EXCEED{Le temps écoulé depuis la dernière synchronisation a dépassé la limite maximale. Veuillez synchroniser depuis le serveur avant de poursuivre cet enregistrement.} OPT_TO_REG_OUTSIDE_LOCATION{Votre emplacement de machine cliente est en dehors du centre d'enregistrement. Veuillez noter que l'enregistrement ne peut être effectué qu'à l'intérieur du centre d'enregistrement} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { diff --git a/assets/l10n/app_hi.arb b/assets/l10n/app_hi.arb index 08a1be72c..e2d14a871 100644 --- a/assets/l10n/app_hi.arb +++ b/assets/l10n/app_hi.arb @@ -302,7 +302,7 @@ "logout_success": "आप सफलता पूर्वक लॉगआउट कर चुके हैं!", "logout_failure": "कुछ गलती हो गई है, कृपया कुछ समय बाद पुनः प्रयास करें", "go_to_home": "होम पर जाएं", -"errors": "{messages, select, REG_TRY_AGAIN{लॉगिन विफल रहा। फिर कोशिश करो!} REG_INVALID_REQUEST{पासवर्ड गलत है!} REG_MACHINE_NOT_FOUND{इस डिवाइस का ऑनबोर्डिंग अभी तक नहीं हुआ है। सहायता के लिए कृपया अपने प्रशासक से संपर्क करें!} REG_NETWORK_ERROR{लॉगिन विफल रहा। नेटवर्क कनेक्शन की जाँच करें!} REG_CRED_EXPIRED{क्रेडेंशियल्स नहीं मिले या समय सीमा समाप्त हो गई है. ऑनलाइन लॉगिन का प्रयास करें!} REG_MACHINE_INACTIVE{मशीन सक्रिय नहीं है!} REG_CENTER_INACTIVE{केंद्र सक्रिय नहीं है!} REG_LOGIN_LOCKED{अधिकतम लॉगिन प्रयास सीमा पूरी हो गई. बाद में पुन: प्रयास!} KER_SYN_AUTH_001{प्रमाणीकरण टोकन प्राप्त करने में असमर्थ!} PAK_APPRVL_MAX_TIME{कार्रवाई आवश्यक: लंबित पंजीकृत पैकेट स्वीकृत अनुमोदन समय से अधिक हो गए हैं। कृपया जारी रखने के लिए बैकलॉग साफ़ करें।} PAK_UPLOAD_MAX_TIME{कार्रवाई आवश्यक: पंजीकरण के साथ आगे बढ़ने से पहले पैकेट को सर्वर पर अपलोड या निर्यात करें।} PAK_UPLOAD_MAX_COUNT{कार्रवाई आवश्यक: पैकेट की सीमा पूरी हो गई. कृपया नए पंजीकरण बनाने से पहले मौजूदा पैकेट निर्यात या अपलोड करें।} other{'Some error occurred!'}}", +"errors": "{messages, select, REG_TRY_AGAIN{लॉगिन विफल रहा। फिर कोशिश करो!} REG_INVALID_REQUEST{पासवर्ड गलत है!} REG_MACHINE_NOT_FOUND{इस डिवाइस का ऑनबोर्डिंग अभी तक नहीं हुआ है। सहायता के लिए कृपया अपने प्रशासक से संपर्क करें!} REG_NETWORK_ERROR{लॉगिन विफल रहा। नेटवर्क कनेक्शन की जाँच करें!} REG_CRED_EXPIRED{क्रेडेंशियल्स नहीं मिले या समय सीमा समाप्त हो गई है. ऑनलाइन लॉगिन का प्रयास करें!} REG_MACHINE_INACTIVE{मशीन सक्रिय नहीं है!} REG_CENTER_INACTIVE{केंद्र सक्रिय नहीं है!} REG_LOGIN_LOCKED{अधिकतम लॉगिन प्रयास सीमा पूरी हो गई. बाद में पुन: प्रयास!} KER_SYN_AUTH_001{प्रमाणीकरण टोकन प्राप्त करने में असमर्थ!} PAK_APPRVL_MAX_TIME{कार्रवाई आवश्यक: लंबित पंजीकृत पैकेट स्वीकृत अनुमोदन समय से अधिक हो गए हैं। कृपया जारी रखने के लिए बैकलॉग साफ़ करें।} PAK_UPLOAD_MAX_TIME{कार्रवाई आवश्यक: पंजीकरण के साथ आगे बढ़ने से पहले पैकेट को सर्वर पर अपलोड या निर्यात करें।} PAK_UPLOAD_MAX_COUNT{कार्रवाई आवश्यक: पैकेट की सीमा पूरी हो गई. कृपया नए पंजीकरण बनाने से पहले मौजूदा पैकेट निर्यात या अपलोड करें।} OPT_TO_REG_TIME_SYNC_EXCEED{अंतिम सिंक के बाद से समय अधिकतम सीमा से अधिक हो गया है। कृपया इस पंजीकरण के साथ आगे बढ़ने से पहले सर्वर से सिंक करें।} OPT_TO_REG_OUTSIDE_LOCATION{आपका क्लाइंट मशीन स्थान पंजीकरण केंद्र के बाहर है। कृपया ध्यान दें कि पंजीकरण केवल पंजीकरण केंद्र के भीतर से किया जा सकता है} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { diff --git a/assets/l10n/app_kn.arb b/assets/l10n/app_kn.arb index db0a4ec1d..33dab81d5 100644 --- a/assets/l10n/app_kn.arb +++ b/assets/l10n/app_kn.arb @@ -302,7 +302,7 @@ "logout_success": "ನೀವು ಯಶಸ್ವಿಯಾಗಿ ಲಾಗ್ ಔಟ್ ಆಗಿರುವಿರಿ!", "logout_failure": "ಏನೋ ತಪ್ಪಾಗಿದೆ, ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", "go_to_home": "ಮನೆಗೆ ಹೋಗು", -"errors": "{messages, select, REG_TRY_AGAIN{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು!} REG_INVALID_REQUEST{ಪಾಸ್ ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ!} REG_MACHINE_NOT_FOUND{ಈ ಸಾಧನವನ್ನು ಇನ್ನೂ ಆನ್‌ಬೋರ್ಡ್ ಮಾಡಿಲ್ಲ. ಸಹಾಯಕ್ಕಾಗಿ ದಯವಿಟ್ಟು ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ!} REG_NETWORK_ERROR{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕ ಪರಿಶೀಲಿಸಿ!} REG_CRED_EXPIRED{ರುಜುವಾತುಗಳು ಸಿಗಲಿಲ್ಲ ಅಥವಾ ಅವಧಿ ಮೀರಿದವು. ದಯವಿಟ್ಟು ಆನ್ ಲೈನ್ ಲಾಗಿನ್ ಪ್ರಯತ್ನಿಸಿ!} REG_MACHINE_INACTIVE{ಯಂತ್ರ ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_CENTER_INACTIVE{ಕೇಂದ್ರವು ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_LOGIN_LOCKED{ಗರಿಷ್ಠ ಲಾಗಿನ್ ಪ್ರಯತ್ನದ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!} KER_SYN_AUTH_001{ದೃಢೀಕರಣ ಟೋಕನ್ ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ!} PAK_APPRVL_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಾಯಿತ ಪ್ಯಾಕೆಟ್‌ಗಳು ಅನುಮತಿಸಲಾದ ಅನುಮೋದನೆ ಸಮಯವನ್ನು ಮೀರಿದೆ. ಮುಂದುವರಿಸಲು ದಯವಿಟ್ಟು ಬ್ಯಾಕ್‌ಲಾಗ್ ಅನ್ನು ತೆರವುಗೊಳಿಸಿ.} PAK_UPLOAD_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಸರ್ವರ್‌ಗೆ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ ಅಥವಾ ರಫ್ತು ಮಾಡಿ.} PAK_UPLOAD_MAX_COUNT{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಪ್ಯಾಕೆಟ್ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ಹೊಸ ನೋಂದಣಿಗಳನ್ನು ರಚಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ರಫ್ತು ಮಾಡಿ ಅಥವಾ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ.} other{'Some error occurred!'}}", +"errors": "{messages, select, REG_TRY_AGAIN{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು!} REG_INVALID_REQUEST{ಪಾಸ್ ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ!} REG_MACHINE_NOT_FOUND{ಈ ಸಾಧನವನ್ನು ಇನ್ನೂ ಆನ್‌ಬೋರ್ಡ್ ಮಾಡಿಲ್ಲ. ಸಹಾಯಕ್ಕಾಗಿ ದಯವಿಟ್ಟು ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ!} REG_NETWORK_ERROR{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕ ಪರಿಶೀಲಿಸಿ!} REG_CRED_EXPIRED{ರುಜುವಾತುಗಳು ಸಿಗಲಿಲ್ಲ ಅಥವಾ ಅವಧಿ ಮೀರಿದವು. ದಯವಿಟ್ಟು ಆನ್ ಲೈನ್ ಲಾಗಿನ್ ಪ್ರಯತ್ನಿಸಿ!} REG_MACHINE_INACTIVE{ಯಂತ್ರ ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_CENTER_INACTIVE{ಕೇಂದ್ರವು ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_LOGIN_LOCKED{ಗರಿಷ್ಠ ಲಾಗಿನ್ ಪ್ರಯತ್ನದ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!} KER_SYN_AUTH_001{ದೃಢೀಕರಣ ಟೋಕನ್ ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ!} PAK_APPRVL_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಾಯಿತ ಪ್ಯಾಕೆಟ್‌ಗಳು ಅನುಮತಿಸಲಾದ ಅನುಮೋದನೆ ಸಮಯವನ್ನು ಮೀರಿದೆ. ಮುಂದುವರಿಸಲು ದಯವಿಟ್ಟು ಬ್ಯಾಕ್‌ಲಾಗ್ ಅನ್ನು ತೆರವುಗೊಳಿಸಿ.} PAK_UPLOAD_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಸರ್ವರ್‌ಗೆ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ ಅಥವಾ ರಫ್ತು ಮಾಡಿ.} PAK_UPLOAD_MAX_COUNT{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಪ್ಯಾಕೆಟ್ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ಹೊಸ ನೋಂದಣಿಗಳನ್ನು ರಚಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ರಫ್ತು ಮಾಡಿ ಅಥವಾ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ.} OPT_TO_REG_TIME_SYNC_EXCEED{ಕೊನೆಯ ಸಿಂಕ್‌ನಿಂದ ಸಮಯವು ಗರಿಷ್ಠ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಈ ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ದಯವಿಟ್ಟು ಸರ್ವರ್‌ನಿಂದ ಸಿಂಕ್ ಮಾಡಿ.} OPT_TO_REG_OUTSIDE_LOCATION{ನಿಮ್ಮ ಕ್ಲೈಂಟ್ ಯಂತ್ರದ ಸ್ಥಳವು ನೋಂದಣಿ ಕೇಂದ್ರದ ಹೊರಗಿದೆ. ನೋಂದಣಿಯನ್ನು ನೋಂದಣಿ ಕೇಂದ್ರದೊಳಗೆ ಮಾತ್ರ ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ದಯವಿಟ್ಟು ಗಮನಿಸಿ} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { diff --git a/assets/l10n/app_ta.arb b/assets/l10n/app_ta.arb index 929e0f550..bb4de1de7 100644 --- a/assets/l10n/app_ta.arb +++ b/assets/l10n/app_ta.arb @@ -311,7 +311,7 @@ "logout_success": "நீங்கள் வெற்றிகரமாக வெளியேறிவிட்டீர்கள்!", "logout_failure": "ஏதோ தவறாகிவிட்டது, சிறிது நேரம் கழித்து மீண்டும் முயற்சிக்கவும்", "go_to_home": "வீட்டிற்கு போ", -"errors": "{messages, select, REG_TRY_AGAIN{உள்நுழைவு தோல்வியுற்றது. மீண்டும் முயற்சி செய்!} REG_INVALID_REQUEST{கடவுச்சொல் தவறானது!} REG_MACHINE_NOT_FOUND{இந்த சாதனம் இன்னும் பதிவுசெய்யப்படவில்லை. உதவிக்காக உங்கள் நிர்வாகியை தொடர்புகொள்ளுங்கள்!} REG_NETWORK_ERROR{உள்நுழைவு தோல்வியுற்றது. நெட்வொர்க் இணைப்பை சரிபார்க்கவும்!} REG_CRED_EXPIRED{நற்சான்றிதழ்கள் காணப்படவில்லை அல்லது காலாவதியாகவில்லை. ஆன்லைன் உள்நுழைவை முயற்சிக்கவும்!} REG_MACHINE_INACTIVE{இயந்திரம் செயலில் இல்லை!} REG_CENTER_INACTIVE{மையம் செயல்படவில்லை!} REG_LOGIN_LOCKED{அதிகபட்ச உள்நுழைவு முயற்சி வரம்பை அடைந்தது. பிறகு முயற்சிக்கவும்!} KER_SYN_AUTH_001{அங்கீகார டோக்கனை பெற முடியவில்லை!} PAK_APPRVL_MAX_TIME{நடவடிக்கை தேவை: நிலுவையிலுள்ள பதிவு செய்யப்பட்ட பாக்கெட்டுகள் அனுமதிக்கப்பட்ட அனுமதி நேரத்தை மீறியுள்ளன. தொடர, பின்னிணைப்பை அழிக்கவும்.} PAK_UPLOAD_MAX_TIME{நடவடிக்கை தேவை: பதிவைத் தொடர்வதற்கு முன், சர்வரில் பாக்கெட்டுகளைப் பதிவேற்றவும் அல்லது ஏற்றுமதி செய்யவும்.} PAK_UPLOAD_MAX_COUNT{நடவடிக்கை தேவை: பாக்கெட் வரம்பை அடைந்தது. புதிய பதிவுகளை உருவாக்கும் முன் ஏற்கனவே உள்ள பாக்கெட்டுகளை ஏற்றுமதி செய்யவும் அல்லது பதிவேற்றவும்.} other{'Some error occurred!'}}", +"errors": "{messages, select, REG_TRY_AGAIN{உள்நுழைவு தோல்வியுற்றது. மீண்டும் முயற்சி செய்!} REG_INVALID_REQUEST{கடவுச்சொல் தவறானது!} REG_MACHINE_NOT_FOUND{இந்த சாதனம் இன்னும் பதிவுசெய்யப்படவில்லை. உதவிக்காக உங்கள் நிர்வாகியை தொடர்புகொள்ளுங்கள்!} REG_NETWORK_ERROR{உள்நுழைவு தோல்வியுற்றது. நெட்வொர்க் இணைப்பை சரிபார்க்கவும்!} REG_CRED_EXPIRED{நற்சான்றிதழ்கள் காணப்படவில்லை அல்லது காலாவதியாகவில்லை. ஆன்லைன் உள்நுழைவை முயற்சிக்கவும்!} REG_MACHINE_INACTIVE{இயந்திரம் செயலில் இல்லை!} REG_CENTER_INACTIVE{மையம் செயல்படவில்லை!} REG_LOGIN_LOCKED{அதிகபட்ச உள்நுழைவு முயற்சி வரம்பை அடைந்தது. பிறகு முயற்சிக்கவும்!} KER_SYN_AUTH_001{அங்கீகார டோக்கனை பெற முடியவில்லை!} PAK_APPRVL_MAX_TIME{நடவடிக்கை தேவை: நிலுவையிலுள்ள பதிவு செய்யப்பட்ட பாக்கெட்டுகள் அனுமதிக்கப்பட்ட அனுமதி நேரத்தை மீறியுள்ளன. தொடர, பின்னிணைப்பை அழிக்கவும்.} PAK_UPLOAD_MAX_TIME{நடவடிக்கை தேவை: பதிவைத் தொடர்வதற்கு முன், சர்வரில் பாக்கெட்டுகளைப் பதிவேற்றவும் அல்லது ஏற்றுமதி செய்யவும்.} PAK_UPLOAD_MAX_COUNT{நடவடிக்கை தேவை: பாக்கெட் வரம்பை அடைந்தது. புதிய பதிவுகளை உருவாக்கும் முன் ஏற்கனவே உள்ள பாக்கெட்டுகளை ஏற்றுமதி செய்யவும் அல்லது பதிவேற்றவும்.} OPT_TO_REG_TIME_SYNC_EXCEED{கடைசி ஒத்திசைவிலிருந்து நேரம் அதிகபட்ச வரம்பை மீறியுள்ளது. இந்த பதிவைத் தொடர்வதற்கு முன் தயவுசெய்து சர்வரிலிருந்து ஒத்திசைக்கவும்.} OPT_TO_REG_OUTSIDE_LOCATION{உங்கள் கிளையன் இயந்திர இடம் பதிவு மையத்திற்கு வெளியே உள்ளது. பதிவு பதிவு மையத்திற்குள் மட்டுமே செய்யப்படலாம் என்பதை தயவுசெய்து கவனிக்கவும்} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": { From 7bcdaee5f17906bd84751c4052646d0e41012cad Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Thu, 8 Jan 2026 16:36:52 +0530 Subject: [PATCH 02/11] coderabbit review fixed Signed-off-by: sachin.sp --- .../clientmanager/service/SyncStatusValidatorServiceImpl.java | 4 ++-- .../clientmanager/spi/SyncStatusValidatorService.java | 4 ++-- android/clientmanager/src/main/res/values/strings.xml | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index 6ab8d781e..c723aa1ad 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -227,7 +227,7 @@ private int getActualDays(Date lastSyncDate) { long millisecondsDifference = new Date().getTime() - lastSyncDate.getTime(); long daysDifference = millisecondsDifference / (24 * 60 * 60 * 1000); - return (int) daysDifference + 1; + return (int) daysDifference; } /** @@ -304,7 +304,7 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach distanceMeters, maxAllowedDistance)); throw new ClientCheckedException( RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, - "Your client machine location is outside the registration center. Please note that registration can be done only from within the registration centre"); + context.getString(R.string.err_outside_registration_center)); } Log.i(TAG, "Location validated successfully - machine is within allowed distance"); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java index 3acf64e92..020ab01d2 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java @@ -17,8 +17,8 @@ public interface SyncStatusValidatorService { /** * Validates machine distance from registration center using GPS. * - * @param machineLongitude Machine longitude - * @param machineLatitude Machine latitude + * @param machineLongitude Machine longitude (validation skipped if null) + * @param machineLatitude Machine latitude (validation skipped if null) * @throws Exception if machine is outside allowed distance */ void validateCenterToMachineDistance(Double machineLongitude, Double machineLatitude) throws Exception; diff --git a/android/clientmanager/src/main/res/values/strings.xml b/android/clientmanager/src/main/res/values/strings.xml index fdcc03d4e..8d4256f84 100644 --- a/android/clientmanager/src/main/res/values/strings.xml +++ b/android/clientmanager/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ No Schema found Registration not started ! Failed to create registration packet + Your client machine location is outside the registration center. Please note that registration can be done only from within the registration centre Minimum required space is not available Registrations are not allowed Policy Certificate Data not allowed From 2e6383d6ad8a4f546622e08f7d318754aa7e1c58 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Thu, 8 Jan 2026 16:45:31 +0530 Subject: [PATCH 03/11] coderabbit review fixed Signed-off-by: sachin.sp --- .../clientmanager/service/SyncStatusValidatorServiceImpl.java | 4 ++-- android/clientmanager/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index c723aa1ad..16d1031ac 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -242,8 +242,8 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach Log.i(TAG, "Validating center to machine distance started"); String enableFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); - if (enableFlag == null || "Y".equalsIgnoreCase(enableFlag)) { - Log.d(TAG, "GPS distance validation disabled or not configured, skipping"); + if (enableFlag == null || !"Y".equalsIgnoreCase(enableFlag)) { + Log.d(TAG, "GPS distance validation not enabled, skipping"); return; } diff --git a/android/clientmanager/src/main/res/values/strings.xml b/android/clientmanager/src/main/res/values/strings.xml index 8d4256f84..c90a1a689 100644 --- a/android/clientmanager/src/main/res/values/strings.xml +++ b/android/clientmanager/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ No Schema found Registration not started ! Failed to create registration packet - Your client machine location is outside the registration center. Please note that registration can be done only from within the registration centre + Your client machine location is outside the registration center. Please note that registration can be done only from within the registration center Minimum required space is not available Registrations are not allowed Policy Certificate Data not allowed From d3e9f33b786125cad4121dc1524d9fe49ebb681a Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Fri, 9 Jan 2026 11:02:15 +0530 Subject: [PATCH 04/11] review comment fixed Signed-off-by: sachin.sp --- .../clientmanager/service/RegistrationServiceImpl.java | 2 +- .../clientmanager/service/SyncStatusValidatorServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index ed9d9b0d1..0e9201e1e 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -197,7 +197,7 @@ public RegistrationDto startRegistration(@NonNull List languages, String } this.registrationDto = new RegistrationDto(rid, flowType, process, version, languages, bioThresholds, rid); - // Validate machine distance from registration center + // Validate machine distance from registration center if location is available if (syncStatusValidatorService != null && this.registrationDto.getGeoLocationDto() != null) { try { syncStatusValidatorService.validateCenterToMachineDistance( diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index 16d1031ac..cdca58b19 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -145,7 +145,7 @@ private void validatingSyncJobsConfig() throws Exception { Log.d(TAG, String.format( "Job [%s] (%s): Configured frequency=%d days, Actual days since last sync=%d", jobId, apiName, configuredFrequency, actualDays)); - if (configuredFrequency <= actualDays) { + if (actualDays > configuredFrequency) { syncFailureCount++; errorDetails.append("- ").append(apiName) .append(": Last sync was ").append(actualDays) From ea32db425001f7651e0ec568a7ae6146044bc84b Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Mon, 19 Jan 2026 12:27:07 +0530 Subject: [PATCH 05/11] upadted the review comment Signed-off-by: sachin.sp --- .../api_services/RegistrationApi.java | 5 +- .../service/RegistrationServiceImpl.java | 23 +- .../SyncStatusValidatorServiceImpl.java | 44 +--- .../spi/RegistrationService.java | 2 +- .../service/RegistrationServiceImplTest.java | 217 +++++++++++++++++- .../registration_service_impl.dart | 6 +- lib/platform_spi/registration_service.dart | 4 +- lib/provider/registration_task_provider.dart | 6 +- lib/ui/process_ui/generic_process.dart | 17 -- .../process_ui/widgets/language_selector.dart | 17 +- pigeon/registration_data.dart | 4 +- 11 files changed, 251 insertions(+), 94 deletions(-) diff --git a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java index 74e3f3533..8379f2aed 100644 --- a/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java +++ b/android/app/src/main/java/io/mosip/registration_client/api_services/RegistrationApi.java @@ -48,11 +48,12 @@ public RegistrationApi(RegistrationService registrationService, TemplateService } @Override - public void startRegistration(@NonNull List languages, @NonNull String flowType, @NonNull String process, @NonNull RegistrationDataPigeon.Result result) { + public void startRegistration(@NonNull List languages, @NonNull String flowType, @NonNull String process, + Double latitude, Double longitude, @NonNull RegistrationDataPigeon.Result result) { auditManagerService.audit(AuditEvent.REGISTRATION_START, Components.REGISTRATION); String response = ""; try { - this.registrationDto = registrationService.startRegistration(languages, flowType, process); + this.registrationDto = registrationService.startRegistration(languages, flowType, process, latitude, longitude); } catch (ClientCheckedException e) { response = e.getErrorCode(); } catch (Exception e) { diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index 0e9201e1e..947b4b354 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -164,7 +164,7 @@ public void rejectRegistration(Registration registration) { } @Override - public RegistrationDto startRegistration(@NonNull List languages, String flowType, String process) throws Exception { + public RegistrationDto startRegistration(@NonNull List languages, String flowType, String process, Double latitude, Double longitude) throws Exception { if (registrationDto != null) { registrationDto.cleanup(); } @@ -197,15 +197,18 @@ public RegistrationDto startRegistration(@NonNull List languages, String } this.registrationDto = new RegistrationDto(rid, flowType, process, version, languages, bioThresholds, rid); - // Validate machine distance from registration center if location is available - if (syncStatusValidatorService != null && this.registrationDto.getGeoLocationDto() != null) { - try { - syncStatusValidatorService.validateCenterToMachineDistance( - this.registrationDto.getGeoLocationDto().getLongitude(), - this.registrationDto.getGeoLocationDto().getLatitude()); - } catch (ClientCheckedException e) { - Log.e(TAG, "Location validation failed", e); - throw e; + // Set GPS location if provided and validate distance from registration center + if (latitude != null && longitude != null) { + this.registrationDto.setGeoLocation(longitude, latitude); + + // Validate machine distance from registration center if location is available + if (syncStatusValidatorService != null) { + try { + syncStatusValidatorService.validateCenterToMachineDistance(longitude, latitude); + } catch (ClientCheckedException e) { + Log.e(TAG, "Location validation failed", e); + throw e; + } } } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index cdca58b19..5b97deff3 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -73,14 +73,8 @@ public SyncStatusValidatorServiceImpl( */ @Override public void validateSyncStatus() throws Exception { - Log.i(TAG, "Validating sync status started"); - try { - // Validate sync job frequencies validatingSyncJobsConfig(); - - Log.i(TAG, "Sync status validation completed successfully"); - } catch (ClientCheckedException e) { Log.e(TAG, "Sync status validation failed", e); throw e; @@ -97,17 +91,13 @@ public void validateSyncStatus() throws Exception { * @throws ClientCheckedException if any job is overdue */ private void validatingSyncJobsConfig() throws Exception { - Log.i(TAG, "Validating sync jobs configuration started"); - List activeJobs = syncJobDefRepository.getActiveSyncJobs(); if (activeJobs == null || activeJobs.isEmpty()) { - Log.w(TAG, "No active sync jobs found, skipping validation"); return; } Map jobFrequencyMap = getSyncJobFrequencies(activeJobs); if (jobFrequencyMap.isEmpty()) { - Log.w(TAG, "No sync job frequencies configured, skipping validation"); return; } int syncFailureCount = 0; @@ -124,7 +114,6 @@ private void validatingSyncJobsConfig() throws Exception { String configuredFrequencyStr = jobFrequencyMap.get(jobId); if (configuredFrequencyStr == null || configuredFrequencyStr.trim().isEmpty()) { - Log.d(TAG, "No frequency configured for job: " + jobId + " (" + apiName + ")"); continue; } @@ -134,26 +123,17 @@ private void validatingSyncJobsConfig() throws Exception { long lastSyncTimeMillis = jobTransactionService.getLastSyncTime(serviceJobId); if (lastSyncTimeMillis == 0) { - Log.d(TAG, "No sync history for job: " + jobId + " (" + apiName + - ") - Skipping validation"); continue; } Date lastSyncDate = new Date(lastSyncTimeMillis); int actualDays = getActualDays(lastSyncDate); - Log.d(TAG, String.format( - "Job [%s] (%s): Configured frequency=%d days, Actual days since last sync=%d", - jobId, apiName, configuredFrequency, actualDays)); if (actualDays > configuredFrequency) { syncFailureCount++; errorDetails.append("- ").append(apiName) .append(": Last sync was ").append(actualDays) .append(" days ago (limit: ").append(configuredFrequency).append(" days)\n"); - - Log.w(TAG, String.format( - "Sync job [%s] (%s) is overdue. Configured: %d days, Actual: %d days since last sync", - jobId, apiName, configuredFrequency, actualDays)); } } catch (NumberFormatException e) { @@ -168,8 +148,6 @@ private void validatingSyncJobsConfig() throws Exception { Log.e(TAG, errorMessage); throw new ClientCheckedException(RegistrationConstants.OPT_TO_REG_TIME_SYNC_EXCEED, errorMessage); } - - Log.i(TAG, "All sync jobs validated successfully"); } /** @@ -179,8 +157,6 @@ private void validatingSyncJobsConfig() throws Exception { * @return Map of jobId to frequency value */ private Map getSyncJobFrequencies(List activeJobs) { - Log.i(TAG, "Fetching sync job frequencies started"); - Map jobsMap = new HashMap<>(); for (SyncJobDef syncJobDef : activeJobs) { @@ -188,7 +164,6 @@ private Map getSyncJobFrequencies(List activeJobs) { String apiName = syncJobDef.getApiName(); if (apiName == null || apiName.trim().isEmpty()) { - Log.w(TAG, "Skipping job with null or empty apiName: " + jobId); continue; } @@ -202,15 +177,9 @@ private Map getSyncJobFrequencies(List activeJobs) { if (configuredValue != null && !configuredValue.trim().isEmpty() && !configuredValue.equalsIgnoreCase("null")) { jobsMap.put(jobId, configuredValue.trim()); - Log.d(TAG, String.format( - "Loaded frequency for job [%s] (%s): %s days", - jobId, apiName, configuredValue.trim())); - } else { - Log.d(TAG, "Frequency property not found for job [" + jobId + "] (" + apiName + "): " + propertyName); } } - Log.i(TAG, "Fetched " + jobsMap.size() + " sync job frequency configurations"); return jobsMap; } @@ -239,16 +208,12 @@ private int getActualDays(Date lastSyncDate) { */ @Override public void validateCenterToMachineDistance(Double machineLongitude, Double machineLatitude) throws Exception { - Log.i(TAG, "Validating center to machine distance started"); - String enableFlag = globalParamRepository.getCachedStringGpsDeviceEnableFlag(); if (enableFlag == null || !"Y".equalsIgnoreCase(enableFlag)) { - Log.d(TAG, "GPS distance validation not enabled, skipping"); return; } if (machineLongitude == null || machineLatitude == null) { - Log.d(TAG, "Machine GPS location not available, skipping validation"); return; } @@ -294,21 +259,16 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach double maxAllowedDistance = Double.parseDouble(maxDistanceStr); - Log.d(TAG, String.format( - "Distance validation: Calculated=%.2f meters, Max allowed=%.2f meters", - distanceMeters, maxAllowedDistance)); - if (distanceMeters > maxAllowedDistance) { Log.e(TAG, String.format( - "Machine is outside allowed distance: %.2f meters (limit: %.2f meters)", + "Distance validation failed - Machine: %.6f,%.6f | Center: %.6f,%.6f | Distance: %.2f m (limit: %.2f m)", + machineLatitude, machineLongitude, centerLatitude, centerLongitude, distanceMeters, maxAllowedDistance)); throw new ClientCheckedException( RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, context.getString(R.string.err_outside_registration_center)); } - Log.i(TAG, "Location validated successfully - machine is within allowed distance"); - } catch (NumberFormatException e) { Log.e(TAG, "Invalid center coordinates format", e); throw new ClientCheckedException(context, R.string.err_004); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/RegistrationService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/RegistrationService.java index 79daf9996..c7cb1680f 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/RegistrationService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/RegistrationService.java @@ -13,7 +13,7 @@ public interface RegistrationService { void rejectRegistration(Registration registration); - RegistrationDto startRegistration(List languages, String flowType, String process) throws Exception; + RegistrationDto startRegistration(List languages, String flowType, String process, Double latitude, Double longitude) throws Exception; RegistrationDto getRegistrationDto() throws Exception; diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java index d9417cb2f..e1785d67c 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java @@ -20,6 +20,7 @@ import io.mosip.registration.clientmanager.spi.PacketService; import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import io.mosip.registration.clientmanager.spi.RegistrationService; +import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; import io.mosip.registration.keymanager.repository.KeyStoreRepository; import io.mosip.registration.keymanager.spi.ClientCryptoManagerService; import io.mosip.registration.packetmanager.spi.PacketWriterService; @@ -61,6 +62,7 @@ import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +104,8 @@ public class RegistrationServiceImplTest { private PreRegistrationDataSyncService preRegistrationDataSyncService; @Mock private PacketService packetService; + @Mock + private SyncStatusValidatorService syncStatusValidatorService; @Before public void setUp() { @@ -113,7 +117,7 @@ public void setUp() { when(preRegistrationDataSyncServiceProvider.get()).thenReturn(preRegistrationDataSyncService); registrationService = new RegistrationServiceImpl(mockApplicationContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService,registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService); + keyStoreRepository, globalParamRepository, auditManagerService,registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, syncStatusValidatorService); } @Test(expected = ClientCheckedException.class) @@ -138,7 +142,7 @@ public void getRegistrationDtoAfterStartingRegistration() throws Exception { File mockFile = mock(File.class); when(mockFile.getUsableSpace()).thenReturn(100l*(1024 * 1024)); when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); - RegistrationDto registrationDto = registrationService.startRegistration(Arrays.asList("eng"), "NEW", "NEW"); + RegistrationDto registrationDto = registrationService.startRegistration(Arrays.asList("eng"), "NEW", "NEW", null, null); RegistrationDto result = registrationService.getRegistrationDto(); Assert.assertNotNull(registrationDto); @@ -172,7 +176,7 @@ public void startAndSubmitRegistration() throws Exception { when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); List selectedLanguages = new ArrayList<>(); selectedLanguages.add("eng"); - RegistrationDto registrationDto = registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + RegistrationDto registrationDto = registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); RegistrationDto result = registrationService.getRegistrationDto(); Assert.assertNotNull(registrationDto); @@ -200,7 +204,7 @@ public void startAndSubmitRegistration() throws Exception { public void startRegistrationWithoutMasterSync_throwException() throws Exception { List selectedLanguages = new ArrayList<>(); selectedLanguages.add("eng"); - registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); } @Test(expected = ClientCheckedException.class) @@ -215,7 +219,7 @@ public void startRegistrationWithoutIDSchema_throwException() throws Exception { centerMachineDto.setMachineStatus(true); centerMachineDto.setMachineRefId("10001_110001"); when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); - registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); } @Test(expected = ClientCheckedException.class) @@ -232,7 +236,7 @@ public void startRegistrationWithoutPolicyKey_throwException() throws Exception when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.4); when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn(null); - registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); } @Test(expected = ClientCheckedException.class) @@ -249,7 +253,7 @@ public void startRegistrationInactiveCenter_throwException() throws Exception { when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.4); when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn(null); - registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); } @Test(expected = ClientCheckedException.class) @@ -266,7 +270,7 @@ public void startRegistrationInactiveMachine_throwException() throws Exception { when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.4); when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn(null); - registrationService.startRegistration(selectedLanguages, "NEW", "NEW"); + registrationService.startRegistration(selectedLanguages, "NEW", "NEW", null, null); } @Test(expected = ClientCheckedException.class) @@ -287,7 +291,7 @@ public void clearRegistration() throws Exception { when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); List languages = new ArrayList<>(); languages.add("eng"); - RegistrationDto registrationDto = registrationService.startRegistration(languages, "NEW", "NEW"); + RegistrationDto registrationDto = registrationService.startRegistration(languages, "NEW", "NEW", null, null); RegistrationDto result = registrationService.getRegistrationDto(); Assert.assertNotNull(registrationDto); @@ -346,7 +350,7 @@ public void testGetAdditionalInfo_StringAndList() throws Exception { when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); - registrationService.startRegistration(new ArrayList<>(Collections.singletonList("eng")), "NEW", "NEW"); + registrationService.startRegistration(new ArrayList<>(Collections.singletonList("eng")), "NEW", "NEW", null, null); Method setRegistrationDto = registrationService.getClass().getDeclaredMethod("getRegistrationDto"); setRegistrationDto.setAccessible(true); @@ -1166,7 +1170,7 @@ public void testStartRegistration_CleanupCalled() throws Exception { when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); - RegistrationDto result = registrationService.startRegistration(languages, "NEW", "NEW"); + RegistrationDto result = registrationService.startRegistration(languages, "NEW", "NEW", null, null); assertNotNull(result); // cleanup() should have been called on dummyDto Mockito.verify(dummyDto).cleanup(); @@ -1846,4 +1850,195 @@ public void testDoPreChecksBeforeRegistration_NullCenterMachineDto() throws Exce assertTrue(cause instanceof ClientCheckedException); } } + + // ========== GPS Location Validation in startRegistration() Tests ========== + + @Test + public void testStartRegistration_WithNullGPS_Success() throws Exception { + // Setup + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Execute with null GPS + RegistrationDto result = registrationService.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", null, null); + + // Verify: Registration succeeds, no validation called + assertNotNull(result); + assertNull(result.getGeoLocationDto()); + verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + } + + @Test + public void testStartRegistration_WithGPS_ValidationPasses_Success() throws Exception { + // Setup + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Setup: Validation passes (no exception thrown) + // Note: SyncStatusValidatorService.validateCenterToMachineDistance doesn't throw when validation passes + + // Execute with GPS coordinates + Double latitude = 12.9716; + Double longitude = 77.5946; + RegistrationDto result = registrationService.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", latitude, longitude); + + // Verify: Registration succeeds, GPS set, validation called + assertNotNull(result); + assertNotNull(result.getGeoLocationDto()); + assertEquals(latitude, result.getGeoLocationDto().getLatitude(), 0.0001); + assertEquals(longitude, result.getGeoLocationDto().getLongitude(), 0.0001); + verify(syncStatusValidatorService).validateCenterToMachineDistance(longitude, latitude); + } + + @Test(expected = ClientCheckedException.class) + public void testStartRegistration_WithGPS_ValidationFails_ThrowsException() throws Exception { + // Setup + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Setup: Validation fails - throw exception + Double latitude = 13.9716; + Double longitude = 78.5946; + ClientCheckedException validationException = new ClientCheckedException( + mockApplicationContext, android.R.string.unknownName, "OPT_TO_REG_OUTSIDE_LOCATION"); + Mockito.doThrow(validationException).when(syncStatusValidatorService) + .validateCenterToMachineDistance(longitude, latitude); + + // Execute - should throw exception + try { + registrationService.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", latitude, longitude); + } catch (ClientCheckedException e) { + // Verify: Validation was called + verify(syncStatusValidatorService).validateCenterToMachineDistance(longitude, latitude); + throw e; + } + } + + @Test + public void testStartRegistration_WithGPS_SyncValidatorNull_SkipsValidation() throws Exception { + // Setup: Create service without SyncStatusValidatorService (null) + RegistrationService serviceWithoutValidator = new RegistrationServiceImpl( + mockApplicationContext, packetWriterService, + registrationRepository, masterDataService, identitySchemaRepository, + clientCryptoManagerService, keyStoreRepository, globalParamRepository, + auditManagerService, registrationCenterRepository, locationValidationService, + preRegistrationDataSyncServiceProvider, biometricService, packetService, null); + + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Execute with GPS but validator is null + Double latitude = 12.9716; + Double longitude = 77.5946; + RegistrationDto result = serviceWithoutValidator.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", latitude, longitude); + + // Verify: Registration succeeds, GPS set, no validation exception + assertNotNull(result); + assertNotNull(result.getGeoLocationDto()); + assertEquals(latitude, result.getGeoLocationDto().getLatitude(), 0.0001); + assertEquals(longitude, result.getGeoLocationDto().getLongitude(), 0.0001); + // SyncStatusValidatorService is null, so validation should be skipped + } + + @Test + public void testStartRegistration_WithGPS_LatitudeNull_SkipsValidation() throws Exception { + // Setup + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Execute with null latitude (partial GPS) + RegistrationDto result = registrationService.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", null, 77.5946); + + // Verify: Registration succeeds, no GPS set, no validation called + assertNotNull(result); + assertNull(result.getGeoLocationDto()); + verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + } + + @Test + public void testStartRegistration_WithGPS_LongitudeNull_SkipsValidation() throws Exception { + // Setup + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId("10001"); + centerMachineDto.setMachineId("110001"); + centerMachineDto.setCenterStatus(true); + centerMachineDto.setMachineStatus(true); + centerMachineDto.setMachineRefId("10001_110001"); + when(masterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(identitySchemaRepository.getLatestSchemaVersion()).thenReturn(1.3); + when(keyStoreRepository.getCertificateData("10001_110001")).thenReturn("dummy_cert"); + when(globalParamRepository.getCachedIntegerGlobalParam(Mockito.anyString())).thenReturn(3); + File mockFile = mock(File.class); + when(mockFile.getUsableSpace()).thenReturn(100L * (1024 * 1024)); + when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); + + // Execute with null longitude (partial GPS) + RegistrationDto result = registrationService.startRegistration( + Arrays.asList("eng"), "NEW", "NEW", 12.9716, null); + + // Verify: Registration succeeds, no GPS set, no validation called + assertNotNull(result); + assertNull(result.getGeoLocationDto()); + verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + } } \ No newline at end of file diff --git a/lib/platform_android/registration_service_impl.dart b/lib/platform_android/registration_service_impl.dart index cbce62dea..5462ae987 100644 --- a/lib/platform_android/registration_service_impl.dart +++ b/lib/platform_android/registration_service_impl.dart @@ -12,12 +12,12 @@ import 'package:registration_client/platform_spi/registration_service.dart'; class RegistrationServiceImpl implements RegistrationService { @override - Future startRegistration( - List langauages, String flowType, String process) async { + Future startRegistration(List languages, String flowType, + String process, double? latitude, double? longitude) async { String registrationStartResponse = ''; try { registrationStartResponse = await RegistrationDataApi() - .startRegistration(langauages, flowType, process); + .startRegistration(languages, flowType, process, latitude, longitude); } on PlatformException { registrationStartResponse = "Something went wrong!"; debugPrint('RegApi call failed'); diff --git a/lib/platform_spi/registration_service.dart b/lib/platform_spi/registration_service.dart index 866db15ce..ef24310f0 100644 --- a/lib/platform_spi/registration_service.dart +++ b/lib/platform_spi/registration_service.dart @@ -9,8 +9,8 @@ import 'package:registration_client/pigeon/registration_data_pigeon.dart'; import 'package:registration_client/platform_android/registration_service_impl.dart'; abstract class RegistrationService { - Future startRegistration( - List languages, String flopwType, String process); + Future startRegistration(List languages, String flowType, + String process, double? latitude, double? longitude); Future evaluateMVELVisible(String fieldData); Future evaluateMVELRequired(String fieldData); Future getPreviewTemplate( diff --git a/lib/provider/registration_task_provider.dart b/lib/provider/registration_task_provider.dart index 2eee264cc..6d3211e3d 100644 --- a/lib/provider/registration_task_provider.dart +++ b/lib/provider/registration_task_provider.dart @@ -134,10 +134,10 @@ class RegistrationTaskProvider with ChangeNotifier { notifyListeners(); } - startRegistration( - List languages, String flowType, String process) async { + startRegistration(List languages, String flowType, String process, + {double? latitude, double? longitude}) async { _registrationStartError = await registrationService.startRegistration( - languages, flowType, process); + languages, flowType, process, latitude, longitude); notifyListeners(); } diff --git a/lib/ui/process_ui/generic_process.dart b/lib/ui/process_ui/generic_process.dart index 9e5521a3c..a8ee190cb 100644 --- a/lib/ui/process_ui/generic_process.dart +++ b/lib/ui/process_ui/generic_process.dart @@ -100,23 +100,6 @@ class _GenericProcessState extends State }, )); _registrationScreenLoadedAudit(); - WidgetsBinding.instance.addPostFrameCallback((_) async { - await _fetchLocation(); - }); - } - - bool _locationFetched = false; - - Future _fetchLocation() async { - if (_locationFetched) return; - _locationFetched = true; - - Position? position = await globalProvider.fetchLocation(); - if (position != null) { - registrationTaskProvider.setCurrentLocation(position.latitude, position.longitude); - } else { - debugPrint("Location unavailable — permission denied or service off."); - } } @override diff --git a/lib/ui/process_ui/widgets/language_selector.dart b/lib/ui/process_ui/widgets/language_selector.dart index c57256cb8..543f89c90 100644 --- a/lib/ui/process_ui/widgets/language_selector.dart +++ b/lib/ui/process_ui/widgets/language_selector.dart @@ -82,13 +82,28 @@ class _LanguageSelectorState extends State { globalProvider.fieldDisplayValues = {}; await globalProvider.fieldValues(widget.newProcess); + // Capture GPS location before starting registration to validate distance + double? latitude; + double? longitude; + try { + final position = await globalProvider.fetchLocation(); + if (position != null) { + latitude = position.latitude; + longitude = position.longitude; + } + } catch (e) { + debugPrint("GPS location capture failed: $e"); + } + List langList = _getRegistrationLanguageList(); await registrationTaskProvider.startRegistration( langList, widget.newProcess.flow! == "UPDATE" ? "Update" : widget.newProcess.flow!, - widget.newProcess.id!); + widget.newProcess.id!, + latitude: latitude, + longitude: longitude); registrationTaskProvider.addDemographicField("preferredLang", globalProvider.fieldInputValue["preferredLang"].toString()); diff --git a/pigeon/registration_data.dart b/pigeon/registration_data.dart index 0632b3379..2ea818aa9 100644 --- a/pigeon/registration_data.dart +++ b/pigeon/registration_data.dart @@ -13,8 +13,8 @@ class RegistrationSubmitResponse { @HostApi() abstract class RegistrationDataApi { @async - String startRegistration( - List languages, String flowType, String process); + String startRegistration(List languages, String flowType, + String process, double? latitude, double? longitude); @async bool evaluateMVELVisible(String fieldData); From a926e238307d08d6cf64349adffc6fd3ad624f29 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Mon, 19 Jan 2026 13:08:19 +0530 Subject: [PATCH 06/11] upadted the review comment Signed-off-by: sachin.sp --- .../service/SyncStatusValidatorServiceImpl.java | 12 +++++++++--- lib/provider/global_provider.dart | 2 ++ lib/ui/process_ui/widgets/language_selector.dart | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index 5b97deff3..3ddd1edcc 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -80,7 +80,9 @@ public void validateSyncStatus() throws Exception { throw e; } catch (Exception e) { Log.e(TAG, "Unexpected error during sync status validation", e); - throw new ClientCheckedException(context, R.string.err_004); + throw new ClientCheckedException( + RegistrationConstants.OPT_TO_REG_TIME_SYNC_EXCEED, + "Sync validation error: " + e.getMessage()); } } @@ -213,8 +215,12 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach return; } + // If GPS is enabled, machine coordinates are required if (machineLongitude == null || machineLatitude == null) { - return; + Log.e(TAG, "GPS validation enabled but machine coordinates not available"); + throw new ClientCheckedException( + RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, + context.getString(R.string.err_outside_registration_center)); } CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); @@ -270,7 +276,7 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach } } catch (NumberFormatException e) { - Log.e(TAG, "Invalid center coordinates format", e); + Log.e(TAG, "Invalid number format in center coordinates or max distance configuration", e); throw new ClientCheckedException(context, R.string.err_004); } } diff --git a/lib/provider/global_provider.dart b/lib/provider/global_provider.dart index 391a1ff69..62cafd0e2 100644 --- a/lib/provider/global_provider.dart +++ b/lib/provider/global_provider.dart @@ -915,8 +915,10 @@ class GlobalProvider with ChangeNotifier { } // Fetch location if permission is granted + // Add timeout to prevent indefinite hanging return await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, + timeLimit: const Duration(seconds: 10), ); } } diff --git a/lib/ui/process_ui/widgets/language_selector.dart b/lib/ui/process_ui/widgets/language_selector.dart index 543f89c90..28e4445c9 100644 --- a/lib/ui/process_ui/widgets/language_selector.dart +++ b/lib/ui/process_ui/widgets/language_selector.dart @@ -86,7 +86,8 @@ class _LanguageSelectorState extends State { double? latitude; double? longitude; try { - final position = await globalProvider.fetchLocation(); + final position = await globalProvider.fetchLocation() + .timeout(const Duration(seconds: 10), onTimeout: () => null); if (position != null) { latitude = position.latitude; longitude = position.longitude; From b31c8b363dc762eb966cdd87dd599e73a3e46f62 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Fri, 23 Jan 2026 15:47:35 +0530 Subject: [PATCH 07/11] upadted the review comment Signed-off-by: sachin.sp --- .../clientmanager/service/SyncStatusValidatorServiceImpl.java | 4 ++-- android/clientmanager/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java index 3ddd1edcc..db5e8b5be 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java @@ -220,7 +220,7 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach Log.e(TAG, "GPS validation enabled but machine coordinates not available"); throw new ClientCheckedException( RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, - context.getString(R.string.err_outside_registration_center)); + context.getString(R.string.err_003)); } CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); @@ -272,7 +272,7 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach distanceMeters, maxAllowedDistance)); throw new ClientCheckedException( RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, - context.getString(R.string.err_outside_registration_center)); + context.getString(R.string.err_003)); } } catch (NumberFormatException e) { diff --git a/android/clientmanager/src/main/res/values/strings.xml b/android/clientmanager/src/main/res/values/strings.xml index c90a1a689..fe2c3a94c 100644 --- a/android/clientmanager/src/main/res/values/strings.xml +++ b/android/clientmanager/src/main/res/values/strings.xml @@ -10,9 +10,9 @@ Language is mandatory to begin registration Required master data not found No Schema found + Your client machine location is outside the registration center. Please note that registration can be done only from within the registration center Registration not started ! Failed to create registration packet - Your client machine location is outside the registration center. Please note that registration can be done only from within the registration center Minimum required space is not available Registrations are not allowed Policy Certificate Data not allowed From e22be5c357f3217bb542d1528327919743f00981 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Tue, 27 Jan 2026 22:52:20 +0530 Subject: [PATCH 08/11] updated the review comments Signed-off-by: sachin.sp --- .../clientmanager/config/AppModule.java | 12 +-- ...java => PreCheckValidatorServiceImpl.java} | 25 +++-- .../service/RegistrationServiceImpl.java | 32 +++---- ...ice.java => PreCheckValidatorService.java} | 4 +- ... => PreCheckValidatorServiceImplTest.java} | 94 ++++++++++++------- .../service/RegistrationServiceImplTest.java | 24 ++--- 6 files changed, 111 insertions(+), 80 deletions(-) rename android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/{SyncStatusValidatorServiceImpl.java => PreCheckValidatorServiceImpl.java} (90%) rename android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/{SyncStatusValidatorService.java => PreCheckValidatorService.java} (85%) rename android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/{SyncStatusValidatorServiceImplTest.java => PreCheckValidatorServiceImplTest.java} (85%) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java index b2ac8480b..a9f413251 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/config/AppModule.java @@ -48,7 +48,7 @@ import io.mosip.registration.clientmanager.service.PreRegistrationDataSyncDaoImpl; import io.mosip.registration.clientmanager.service.PreRegistrationDataSyncServiceImpl; import io.mosip.registration.clientmanager.service.RegistrationServiceImpl; -import io.mosip.registration.clientmanager.service.SyncStatusValidatorServiceImpl; +import io.mosip.registration.clientmanager.service.PreCheckValidatorServiceImpl; import io.mosip.registration.clientmanager.service.TemplateService; import io.mosip.registration.clientmanager.service.UserOnboardService; import io.mosip.registration.clientmanager.service.external.PreRegZipHandlingService; @@ -63,7 +63,7 @@ import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import io.mosip.registration.clientmanager.spi.RegistrationService; import io.mosip.registration.clientmanager.spi.SyncRestService; -import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; +import io.mosip.registration.clientmanager.spi.PreCheckValidatorService; import io.mosip.registration.clientmanager.util.DateUtil; import io.mosip.registration.clientmanager.util.SyncRestUtil; import io.mosip.registration.clientmanager.util.UserInterfaceHelperService; @@ -210,10 +210,10 @@ RegistrationService provideRegistrationService(PacketWriterService packetWriterS Provider preRegistrationDataSyncServiceProvider, Biometrics095Service biometricService, PacketService packetService, - SyncStatusValidatorService syncStatusValidatorService) { + PreCheckValidatorService preCheckValidatorService) { return new RegistrationServiceImpl(appContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService, registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, syncStatusValidatorService); + keyStoreRepository, globalParamRepository, auditManagerService, registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, preCheckValidatorService); } @Provides @@ -316,14 +316,14 @@ PreRegistrationList PreRegistrationList() { @Provides @Singleton - SyncStatusValidatorService provideSyncStatusValidatorService(SyncJobDefRepository syncJobDefRepository, + PreCheckValidatorService providePreCheckValidatorService(SyncJobDefRepository syncJobDefRepository, GlobalParamRepository globalParamRepository, JobManagerService jobManagerService, JobTransactionService jobTransactionService, LocationValidationService locationValidationService, MasterDataService masterDataService, RegistrationCenterRepository registrationCenterRepository) { - return new SyncStatusValidatorServiceImpl(appContext, syncJobDefRepository, globalParamRepository, + return new PreCheckValidatorServiceImpl(appContext, syncJobDefRepository, globalParamRepository, jobManagerService, jobTransactionService, locationValidationService, masterDataService, registrationCenterRepository); } diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImpl.java similarity index 90% rename from android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java rename to android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImpl.java index db5e8b5be..ea98a6284 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImpl.java @@ -25,17 +25,17 @@ import io.mosip.registration.clientmanager.spi.JobTransactionService; import io.mosip.registration.clientmanager.spi.LocationValidationService; import io.mosip.registration.clientmanager.spi.MasterDataService; -import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; +import io.mosip.registration.clientmanager.spi.PreCheckValidatorService; /** - * Validates sync status before registration. + * Validates pre-check requirements (sync status and GPS location). * * @author Sachin S P */ @Singleton -public class SyncStatusValidatorServiceImpl implements SyncStatusValidatorService { +public class PreCheckValidatorServiceImpl implements PreCheckValidatorService { - private static final String TAG = SyncStatusValidatorServiceImpl.class.getSimpleName(); + private static final String TAG = PreCheckValidatorServiceImpl.class.getSimpleName(); private Context context; private SyncJobDefRepository syncJobDefRepository; @@ -47,7 +47,7 @@ public class SyncStatusValidatorServiceImpl implements SyncStatusValidatorServic private RegistrationCenterRepository registrationCenterRepository; @Inject - public SyncStatusValidatorServiceImpl( + public PreCheckValidatorServiceImpl( Context context, SyncJobDefRepository syncJobDefRepository, GlobalParamRepository globalParamRepository, @@ -125,6 +125,7 @@ private void validatingSyncJobsConfig() throws Exception { long lastSyncTimeMillis = jobTransactionService.getLastSyncTime(serviceJobId); if (lastSyncTimeMillis == 0) { + // Job has never been synced - skip validation continue; } @@ -140,8 +141,14 @@ private void validatingSyncJobsConfig() throws Exception { } catch (NumberFormatException e) { Log.e(TAG, "Invalid frequency value for job: " + jobId + " (" + apiName + "): " + configuredFrequencyStr, e); + syncFailureCount++; + errorDetails.append("- ").append(apiName) + .append(": Invalid frequency configuration value: ").append(configuredFrequencyStr).append("\n"); } catch (Exception e) { Log.e(TAG, "Error validating job: " + jobId + " (" + apiName + ")", e); + syncFailureCount++; + errorDetails.append("- ").append(apiName) + .append(": Validation error: ").append(e.getMessage()).append("\n"); } } if (syncFailureCount > 0) { @@ -225,16 +232,16 @@ public void validateCenterToMachineDistance(Double machineLongitude, Double mach CenterMachineDto centerMachineDto = masterDataService.getRegistrationCenterMachineDetails(); if (centerMachineDto == null) { - Log.w(TAG, "Center details not found, skipping distance validation"); - return; + Log.e(TAG, "GPS validation enabled but center details not found"); + throw new ClientCheckedException(context, R.string.err_004); } List centers = registrationCenterRepository.getRegistrationCenter( centerMachineDto.getCenterId()); if (centers == null || centers.isEmpty()) { - Log.w(TAG, "Center not found, skipping distance validation"); - return; + Log.e(TAG, "GPS validation enabled but center not found"); + throw new ClientCheckedException(context, R.string.err_004); } RegistrationCenter center = centers.get(0); diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index 35bbda017..fabd7fb21 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -69,7 +69,7 @@ import io.mosip.registration.clientmanager.spi.MasterDataService; import io.mosip.registration.clientmanager.spi.RegistrationService; import io.mosip.registration.clientmanager.spi.PacketService; -import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; +import io.mosip.registration.clientmanager.spi.PreCheckValidatorService; import io.mosip.registration.clientmanager.entity.PreRegistrationList; import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import javax.inject.Provider; @@ -116,7 +116,7 @@ public class RegistrationServiceImpl implements RegistrationService { private LocationValidationService locationValidationService; private Provider preRegistrationDataSyncServiceProvider; private PacketService packetService; - private SyncStatusValidatorService syncStatusValidatorService; + private PreCheckValidatorService preCheckValidatorService; public static final String BOOLEAN_FALSE = "false"; private Biometrics095Service biometricService; @@ -135,7 +135,7 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter Provider preRegistrationDataSyncServiceProvider, Biometrics095Service biometricService, PacketService packetService, - SyncStatusValidatorService syncStatusValidatorService) { + PreCheckValidatorService preCheckValidatorService) { this.context = context; this.registrationDto = null; this.packetWriterService = packetWriterService; @@ -151,7 +151,7 @@ public RegistrationServiceImpl(Context context, PacketWriterService packetWriter this.preRegistrationDataSyncServiceProvider = preRegistrationDataSyncServiceProvider; this.biometricService = biometricService; this.packetService = packetService; - this.syncStatusValidatorService = syncStatusValidatorService; + this.preCheckValidatorService = preCheckValidatorService; } @Override @@ -198,18 +198,18 @@ public RegistrationDto startRegistration(@NonNull List languages, String } this.registrationDto = new RegistrationDto(rid, flowType, process, version, languages, bioThresholds, rid); - // Set GPS location if provided and validate distance from registration center + // Set GPS location if provided if (latitude != null && longitude != null) { this.registrationDto.setGeoLocation(longitude, latitude); - - // Validate machine distance from registration center if location is available - if (syncStatusValidatorService != null) { - try { - syncStatusValidatorService.validateCenterToMachineDistance(longitude, latitude); - } catch (ClientCheckedException e) { - Log.e(TAG, "Location validation failed", e); - throw e; - } + } + + // Validate GPS location if flag is enabled (even if coordinates are null) + if (preCheckValidatorService != null) { + try { + preCheckValidatorService.validateCenterToMachineDistance(longitude, latitude); + } catch (ClientCheckedException e) { + Log.e(TAG, "Location validation failed", e); + throw e; } } @@ -591,8 +591,8 @@ private void doPreChecksBeforeRegistration(CenterMachineDto centerMachineDto) th throw new ClientCheckedException(context, R.string.err_007); // validate sync status - checks if all sync jobs ran within configured time limits - if (syncStatusValidatorService != null) { - syncStatusValidatorService.validateSyncStatus(); + if (preCheckValidatorService != null) { + preCheckValidatorService.validateSyncStatus(); } // registered packet approval time breach check diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java similarity index 85% rename from android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java rename to android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java index 020ab01d2..16a1175b9 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/SyncStatusValidatorService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java @@ -1,11 +1,11 @@ package io.mosip.registration.clientmanager.spi; /** - * Validates sync status before registration. + * Validates pre-check requirements (sync status and GPS location). * * @author Sachin S P */ -public interface SyncStatusValidatorService { +public interface PreCheckValidatorService { /** * Validates sync job frequencies. diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImplTest.java similarity index 85% rename from android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java rename to android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImplTest.java index 4e0fe1d26..358aa4672 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/SyncStatusValidatorServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/PreCheckValidatorServiceImplTest.java @@ -35,12 +35,12 @@ import io.mosip.registration.clientmanager.spi.MasterDataService; /** - * Unit tests for SyncStatusValidatorServiceImpl. + * Unit tests for PreCheckValidatorServiceImpl. * * @author Sachin S P */ @RunWith(MockitoJUnitRunner.class) -public class SyncStatusValidatorServiceImplTest { +public class PreCheckValidatorServiceImplTest { @Mock private Context mockContext; @@ -73,7 +73,7 @@ public class SyncStatusValidatorServiceImplTest { private SharedPreferences.Editor mockEditor; @InjectMocks - private SyncStatusValidatorServiceImpl syncStatusValidatorService; + private PreCheckValidatorServiceImpl preCheckValidatorService; private static final String JOB_ID_1 = "MDS_J00001"; private static final String JOB_ID_2 = "PDS_J00003"; @@ -117,7 +117,7 @@ public void testValidateSyncStatus_Success_AllJobsWithinLimit() throws Exception when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_2)).thenReturn(oneDayAgo); // Execute - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); // Verify: No exception thrown verify(mockSyncJobDefRepository).getActiveSyncJobs(); @@ -140,7 +140,7 @@ public void testValidateSyncStatus_Failure_JobOverdue() throws Exception { when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(twoDaysAgo); // Execute - should throw exception - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); } @Test @@ -149,7 +149,7 @@ public void testValidateSyncStatus_NoActiveJobs() throws Exception { when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(new ArrayList<>()); // Execute - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); // Verify: No exception, validation skipped verify(mockSyncJobDefRepository).getActiveSyncJobs(); @@ -166,12 +166,12 @@ public void testValidateSyncStatus_JobWithoutSyncHistory_Skipped() throws Except RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) .thenReturn("190"); - // Setup: No sync history (returns 0) + // Setup: No sync history (returns 0) - job should be skipped when(mockJobManagerService.generateJobServiceId(JOB_ID_1)).thenReturn(SERVICE_JOB_ID_1); when(mockJobTransactionService.getLastSyncTime(SERVICE_JOB_ID_1)).thenReturn(0L); - // Execute - syncStatusValidatorService.validateSyncStatus(); + // Execute - should not throw exception, job without history is skipped + preCheckValidatorService.validateSyncStatus(); // Verify: No exception - job without history is skipped verify(mockSyncJobDefRepository).getActiveSyncJobs(); @@ -187,7 +187,7 @@ public void testValidateSyncStatus_NoFrequencyConfigured_Skipped() throws Except when(mockGlobalParamRepository.getCachedStringGlobalParam(anyString())).thenReturn(null); // Execute - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); // Verify: No exception - job without frequency is skipped verify(mockSyncJobDefRepository).getActiveSyncJobs(); @@ -230,7 +230,7 @@ public void testValidateSyncStatus_MultipleJobsOverdue() throws Exception { // Execute - should throw exception try { - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); } catch (ClientCheckedException e) { // Verify error code assertEquals(RegistrationConstants.OPT_TO_REG_TIME_SYNC_EXCEED, e.getErrorCode()); @@ -247,7 +247,7 @@ public void testValidateCenterToMachineDistance_GPSDisabled_Skipped() throws Exc when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("Y"); // Execute - syncStatusValidatorService.validateCenterToMachineDistance(77.5946, 12.9716); + preCheckValidatorService.validateCenterToMachineDistance(77.5946, 12.9716); // Verify: Validation skipped verify(mockGlobalParamRepository).getCachedStringGpsDeviceEnableFlag(); @@ -260,7 +260,7 @@ public void testValidateCenterToMachineDistance_LocationNull_Skipped() throws Ex when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); // Execute with null location - syncStatusValidatorService.validateCenterToMachineDistance(null, null); + preCheckValidatorService.validateCenterToMachineDistance(null, null); // Verify: Validation skipped verify(mockLocationValidationService, never()).getDistance(anyDouble(), anyDouble(), anyDouble(), anyDouble()); @@ -293,7 +293,7 @@ public void testValidateCenterToMachineDistance_WithinDistance_Success() throws when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn("500"); // Execute - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); // Verify: No exception thrown verify(mockLocationValidationService).getDistance(78.5946, 13.9716, 77.5946, 12.9716); @@ -327,7 +327,7 @@ public void testValidateCenterToMachineDistance_OutsideDistance_ThrowsException( // Execute - should throw exception try { - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); } catch (ClientCheckedException e) { // Verify error code assertEquals(RegistrationConstants.OPT_TO_REG_OUTSIDE_LOCATION, e.getErrorCode()); @@ -335,19 +335,46 @@ public void testValidateCenterToMachineDistance_OutsideDistance_ThrowsException( } } - @Test - public void testValidateCenterToMachineDistance_CenterDetailsNotFound_Skipped() throws Exception { + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_CenterDetailsNotFound_ThrowsException() throws Exception { // Setup: GPS enabled - when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("N"); + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("Y"); // Setup: Center details not found when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(null); - // Execute - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + // Execute - should throw exception + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + } - // Verify: Validation skipped - verify(mockLocationValidationService, never()).getDistance(anyDouble(), anyDouble(), anyDouble(), anyDouble()); + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_CenterNotFound_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("Y"); + + // Setup: Center details found but center not found in repository + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(null); + + // Execute - should throw exception + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + } + + @Test(expected = ClientCheckedException.class) + public void testValidateCenterToMachineDistance_EmptyCentersList_ThrowsException() throws Exception { + // Setup: GPS enabled + when(mockGlobalParamRepository.getCachedStringGpsDeviceEnableFlag()).thenReturn("Y"); + + // Setup: Center details found but empty centers list + CenterMachineDto centerMachineDto = new CenterMachineDto(); + centerMachineDto.setCenterId(CENTER_ID); + when(mockMasterDataService.getRegistrationCenterMachineDetails()).thenReturn(centerMachineDto); + when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(new ArrayList<>()); + + // Execute - should throw exception + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); } @Test(expected = ClientCheckedException.class) @@ -368,7 +395,7 @@ public void testValidateCenterToMachineDistance_CenterCoordinatesMissing_ThrowsE when(mockRegistrationCenterRepository.getRegistrationCenter(CENTER_ID)).thenReturn(centers); // Execute - should throw exception - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); } @Test(expected = ClientCheckedException.class) @@ -398,7 +425,7 @@ public void testValidateCenterToMachineDistance_MaxDistanceConfigMissing_ThrowsE when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn(null); // Execute - should throw exception - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); } @Test @@ -422,7 +449,7 @@ public void testValidateCenterToMachineDistance_InvalidCoordinateFormat_ThrowsEx // Execute - should throw exception try { - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); fail("Expected ClientCheckedException for invalid coordinate format"); } catch (ClientCheckedException e) { // Expected @@ -442,7 +469,7 @@ public void testValidateSyncStatus_JobWithNullId_Skipped() throws Exception { when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); // Execute - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); // Verify: No exception, job skipped verify(mockSyncJobDefRepository).getActiveSyncJobs(); @@ -461,14 +488,14 @@ public void testValidateSyncStatus_JobWithNullApiName_Skipped() throws Exception when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); // Execute - syncStatusValidatorService.validateSyncStatus(); + preCheckValidatorService.validateSyncStatus(); // Verify: No exception, job skipped verify(mockSyncJobDefRepository).getActiveSyncJobs(); } - @Test - public void testValidateSyncStatus_InvalidFrequencyValue_ContinuesWithOtherJobs() throws Exception { + @Test(expected = ClientCheckedException.class) + public void testValidateSyncStatus_InvalidFrequencyValue_ThrowsException() throws Exception { // Setup: Create active jobs List activeJobs = createActiveJobs(); when(mockSyncJobDefRepository.getActiveSyncJobs()).thenReturn(activeJobs); @@ -478,11 +505,8 @@ public void testValidateSyncStatus_InvalidFrequencyValue_ContinuesWithOtherJobs( RegistrationConstants.MOSIP_REGISTRATION + API_NAME_1 + RegistrationConstants.DOT + RegistrationConstants.FREQUENCY)) .thenReturn("invalid"); - // Execute - syncStatusValidatorService.validateSyncStatus(); - - // Verify: No exception, continues with other jobs - verify(mockSyncJobDefRepository).getActiveSyncJobs(); + // Execute - should throw exception due to invalid frequency value + preCheckValidatorService.validateSyncStatus(); } @Test @@ -512,7 +536,7 @@ public void testValidateCenterToMachineDistance_ExactDistanceLimit_Passes() thro when(mockGlobalParamRepository.getCachedStringMachineToCenterDistance()).thenReturn("500"); // Execute - should pass (distance == limit, not > limit) - syncStatusValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); + preCheckValidatorService.validateCenterToMachineDistance(78.5946, 13.9716); // Verify: No exception thrown verify(mockLocationValidationService).getDistance(78.5946, 13.9716, 77.5946, 12.9716); diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java index e1785d67c..49afa6e25 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java @@ -20,7 +20,7 @@ import io.mosip.registration.clientmanager.spi.PacketService; import io.mosip.registration.clientmanager.spi.PreRegistrationDataSyncService; import io.mosip.registration.clientmanager.spi.RegistrationService; -import io.mosip.registration.clientmanager.spi.SyncStatusValidatorService; +import io.mosip.registration.clientmanager.spi.PreCheckValidatorService; import io.mosip.registration.keymanager.repository.KeyStoreRepository; import io.mosip.registration.keymanager.spi.ClientCryptoManagerService; import io.mosip.registration.packetmanager.spi.PacketWriterService; @@ -105,7 +105,7 @@ public class RegistrationServiceImplTest { @Mock private PacketService packetService; @Mock - private SyncStatusValidatorService syncStatusValidatorService; + private PreCheckValidatorService preCheckValidatorService; @Before public void setUp() { @@ -117,7 +117,7 @@ public void setUp() { when(preRegistrationDataSyncServiceProvider.get()).thenReturn(preRegistrationDataSyncService); registrationService = new RegistrationServiceImpl(mockApplicationContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, clientCryptoManagerService, - keyStoreRepository, globalParamRepository, auditManagerService,registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, syncStatusValidatorService); + keyStoreRepository, globalParamRepository, auditManagerService,registrationCenterRepository,locationValidationService, preRegistrationDataSyncServiceProvider, biometricService, packetService, preCheckValidatorService); } @Test(expected = ClientCheckedException.class) @@ -1877,7 +1877,7 @@ public void testStartRegistration_WithNullGPS_Success() throws Exception { // Verify: Registration succeeds, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); } @Test @@ -1898,7 +1898,7 @@ public void testStartRegistration_WithGPS_ValidationPasses_Success() throws Exce when(mockApplicationContext.getExternalCacheDir()).thenReturn(mockFile); // Setup: Validation passes (no exception thrown) - // Note: SyncStatusValidatorService.validateCenterToMachineDistance doesn't throw when validation passes + // Note: PreCheckValidatorService.validateCenterToMachineDistance doesn't throw when validation passes // Execute with GPS coordinates Double latitude = 12.9716; @@ -1911,7 +1911,7 @@ public void testStartRegistration_WithGPS_ValidationPasses_Success() throws Exce assertNotNull(result.getGeoLocationDto()); assertEquals(latitude, result.getGeoLocationDto().getLatitude(), 0.0001); assertEquals(longitude, result.getGeoLocationDto().getLongitude(), 0.0001); - verify(syncStatusValidatorService).validateCenterToMachineDistance(longitude, latitude); + verify(preCheckValidatorService).validateCenterToMachineDistance(longitude, latitude); } @Test(expected = ClientCheckedException.class) @@ -1936,7 +1936,7 @@ public void testStartRegistration_WithGPS_ValidationFails_ThrowsException() thro Double longitude = 78.5946; ClientCheckedException validationException = new ClientCheckedException( mockApplicationContext, android.R.string.unknownName, "OPT_TO_REG_OUTSIDE_LOCATION"); - Mockito.doThrow(validationException).when(syncStatusValidatorService) + Mockito.doThrow(validationException).when(preCheckValidatorService) .validateCenterToMachineDistance(longitude, latitude); // Execute - should throw exception @@ -1945,14 +1945,14 @@ public void testStartRegistration_WithGPS_ValidationFails_ThrowsException() thro Arrays.asList("eng"), "NEW", "NEW", latitude, longitude); } catch (ClientCheckedException e) { // Verify: Validation was called - verify(syncStatusValidatorService).validateCenterToMachineDistance(longitude, latitude); + verify(preCheckValidatorService).validateCenterToMachineDistance(longitude, latitude); throw e; } } @Test public void testStartRegistration_WithGPS_SyncValidatorNull_SkipsValidation() throws Exception { - // Setup: Create service without SyncStatusValidatorService (null) + // Setup: Create service without PreCheckValidatorService (null) RegistrationService serviceWithoutValidator = new RegistrationServiceImpl( mockApplicationContext, packetWriterService, registrationRepository, masterDataService, identitySchemaRepository, @@ -1985,7 +1985,7 @@ public void testStartRegistration_WithGPS_SyncValidatorNull_SkipsValidation() th assertNotNull(result.getGeoLocationDto()); assertEquals(latitude, result.getGeoLocationDto().getLatitude(), 0.0001); assertEquals(longitude, result.getGeoLocationDto().getLongitude(), 0.0001); - // SyncStatusValidatorService is null, so validation should be skipped + // PreCheckValidatorService is null, so validation should be skipped } @Test @@ -2012,7 +2012,7 @@ public void testStartRegistration_WithGPS_LatitudeNull_SkipsValidation() throws // Verify: Registration succeeds, no GPS set, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); } @Test @@ -2039,6 +2039,6 @@ public void testStartRegistration_WithGPS_LongitudeNull_SkipsValidation() throws // Verify: Registration succeeds, no GPS set, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(syncStatusValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); } } \ No newline at end of file From ca142e68c3580b6e400837c24ae5a3d0d279f0d2 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Tue, 27 Jan 2026 23:13:00 +0530 Subject: [PATCH 09/11] updated the review comments Signed-off-by: sachin.sp --- .../clientmanager/spi/PreCheckValidatorService.java | 7 ++++--- .../service/RegistrationServiceImplTest.java | 6 +++--- lib/provider/global_provider.dart | 13 +++++++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java index 16a1175b9..27bd203d5 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/spi/PreCheckValidatorService.java @@ -16,10 +16,11 @@ public interface PreCheckValidatorService { /** * Validates machine distance from registration center using GPS. + * Validation is skipped if GPS is disabled. If GPS is enabled, both coordinates must be non-null. * - * @param machineLongitude Machine longitude (validation skipped if null) - * @param machineLatitude Machine latitude (validation skipped if null) - * @throws Exception if machine is outside allowed distance + * @param machineLongitude Machine longitude (required if GPS enabled) + * @param machineLatitude Machine latitude (required if GPS enabled) + * @throws Exception if GPS is enabled and coordinates are null, or if machine is outside allowed distance */ void validateCenterToMachineDistance(Double machineLongitude, Double machineLatitude) throws Exception; } diff --git a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java index 49afa6e25..6d07b46b1 100644 --- a/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java +++ b/android/clientmanager/src/test/java/io/mosip/registration/clientmanager/service/RegistrationServiceImplTest.java @@ -1877,7 +1877,7 @@ public void testStartRegistration_WithNullGPS_Success() throws Exception { // Verify: Registration succeeds, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(any(), any()); } @Test @@ -2012,7 +2012,7 @@ public void testStartRegistration_WithGPS_LatitudeNull_SkipsValidation() throws // Verify: Registration succeeds, no GPS set, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(any(), any()); } @Test @@ -2039,6 +2039,6 @@ public void testStartRegistration_WithGPS_LongitudeNull_SkipsValidation() throws // Verify: Registration succeeds, no GPS set, no validation called assertNotNull(result); assertNull(result.getGeoLocationDto()); - verify(preCheckValidatorService, never()).validateCenterToMachineDistance(anyDouble(), anyDouble()); + verify(preCheckValidatorService, never()).validateCenterToMachineDistance(any(), any()); } } \ No newline at end of file diff --git a/lib/provider/global_provider.dart b/lib/provider/global_provider.dart index 930fccbbc..6b5dae46d 100644 --- a/lib/provider/global_provider.dart +++ b/lib/provider/global_provider.dart @@ -5,6 +5,7 @@ * */ +import 'dart:async'; import 'dart:developer'; import 'package:flutter/services.dart'; @@ -942,9 +943,13 @@ class GlobalProvider with ChangeNotifier { // Fetch location if permission is granted // Add timeout to prevent indefinite hanging - return await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high, - timeLimit: const Duration(seconds: 10), - ); + try { + return await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high, + timeLimit: const Duration(seconds: 10), + ); + } on TimeoutException { + return null; + } } } From dbe04877cd6bf99e86015794eef594d8f5faa626 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Wed, 28 Jan 2026 16:26:35 +0530 Subject: [PATCH 10/11] added review comment Signed-off-by: sachin.sp --- .../service/RegistrationServiceImpl.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java index fabd7fb21..bf92aa2f4 100644 --- a/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java +++ b/android/clientmanager/src/main/java/io/mosip/registration/clientmanager/service/RegistrationServiceImpl.java @@ -198,19 +198,14 @@ public RegistrationDto startRegistration(@NonNull List languages, String } this.registrationDto = new RegistrationDto(rid, flowType, process, version, languages, bioThresholds, rid); - // Set GPS location if provided - if (latitude != null && longitude != null) { - this.registrationDto.setGeoLocation(longitude, latitude); - } + this.registrationDto.setGeoLocation(longitude, latitude); // Validate GPS location if flag is enabled (even if coordinates are null) - if (preCheckValidatorService != null) { - try { + try { preCheckValidatorService.validateCenterToMachineDistance(longitude, latitude); } catch (ClientCheckedException e) { Log.e(TAG, "Location validation failed", e); throw e; - } } SharedPreferences.Editor editor = this.context.getSharedPreferences(this.context.getString(R.string.app_name), @@ -591,9 +586,7 @@ private void doPreChecksBeforeRegistration(CenterMachineDto centerMachineDto) th throw new ClientCheckedException(context, R.string.err_007); // validate sync status - checks if all sync jobs ran within configured time limits - if (preCheckValidatorService != null) { - preCheckValidatorService.validateSyncStatus(); - } + preCheckValidatorService.validateSyncStatus(); // registered packet approval time breach check if (packetService != null && packetService.isRegisteredPacketApprovalTimeBreached()) { From 60c8a913fc724a83488e7b69f836af354951ee04 Mon Sep 17 00:00:00 2001 From: "sachin.sp" Date: Wed, 28 Jan 2026 17:07:42 +0530 Subject: [PATCH 11/11] revert the changes Signed-off-by: sachin.sp --- assets/l10n/app_kn.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/app_kn.arb b/assets/l10n/app_kn.arb index 93a9a6dc8..3349896cc 100644 --- a/assets/l10n/app_kn.arb +++ b/assets/l10n/app_kn.arb @@ -302,7 +302,7 @@ "logout_success": "ನೀವು ಯಶಸ್ವಿಯಾಗಿ ಲಾಗ್ ಔಟ್ ಆಗಿರುವಿರಿ!", "logout_failure": "ಏನೋ ತಪ್ಪಾಗಿದೆ, ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ", "go_to_home": "ಮನೆಗೆ ಹೋಗು", - "errors": "{messages, select, REG_TRY_AGAIN{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು!} REG_INVALID_REQUEST{ಪಾಸ್ ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ!} REG_MACHINE_NOT_FOUND{ಈ ಸಾಧನವನ್ನು ಇನ್ನೂ ಆನ್‌ಬೋರ್ಡ್ ಮಾಡಿಲ್ಲ. ಸಹಾಯಕ್ಕಾಗಿ ದಯವಿಟ್ಟು ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ!} REG_NETWORK_ERROR{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕ ಪರಿಶೀಲಿಸಿ!} REG_CRED_EXPIRED{ರುಜುವಾತುಗಳು ಸಿಗಲಿಲ್ಲ ಅಥವಾ ಅವಧಿ ಮೀರಿದವು. ದಯವಿಟ್ಟು ಆನ್ ಲೈನ್ ಲಾಗಿನ್ ಪ್ರಯತ್ನಿಸಿ!} REG_MACHINE_INACTIVE{ಯಂತ್ರ ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_CENTER_INACTIVE{ಕೇಂದ್ರವು ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_LOGIN_LOCKED{ಗರಿಷ್ಠ ಲಾಗಿನ್ ಪ್ರಯತ್ನದ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!} KER_SYN_AUTH_001{ದೃಢೀಕರಣ ಟೋಕನ್ ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ!} PAK_APPRVL_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಾಯಿತ ಪ್ಯಾಕೆಟ್‌ಗಳು ಅನುಮತಿಸಲಾದ ಅನುಮೋದನೆ ಸಮಯವನ್ನು ಮೀರಿದೆ. ಮುಂದುವರಿಸಲು ದಯವಿಟ್ಟು ಬ್ಯಾಕ್‌ಲಾಗ್ ಅನ್ನು ತೆರವುಗೊಳಿಸಿ.} PAK_UPLOAD_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಸರ್ವರ್‌ಗೆ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ ಅಥವಾ ರಫ್ತು ಮಾಡಿ.} PAK_UPLOAD_MAX_COUNT{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಪ್ಯಾಕೆಟ್ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ಹೊಸ ನೋಂದಣಿಗಳನ್ನು ರಚಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ರಫ್ತು ಮಾಡಿ ಅಥವಾ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ.} PAK_DISK_SPACE_LOW{ಪ್ಯಾಕೆಟ್ ರಚಿಸಲು ಅಗತ್ಯವಿರುವ ಕನಿಷ್ಟ ಸ್ಥಳಾವಕಾಶ ಲಭ್ಯವಿಲ್ಲ.}REG_PKT_APPRVL_CNT_EXCEED{ಕ್ಲೈಂಟ್‌ನಲ್ಲಿ ಅನುಮೋದನೆಗಾಗಿ ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಣಿ ಪ್ಯಾಕೆಟ್‌ಗಳ ಗರಿಷ್ಠ ಸಂಖ್ಯೆಯನ್ನು ತಲುಪಿದೆ. ಈ ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಅನುಮೋದಿಸಿ ಅಥವಾ ತಿರಸ್ಕರಿಸಿ.} OPT_TO_REG_TIME_SYNC_EXCEED{ಕೊನೆಯ ಸಿಂಕ್‌ನಿಂದ ಸಮಯವು ಗರಿಷ್ಠ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಈ ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ದಯವಿಟ್ಟು ಸರ್ವರ್‌ನಿಂದ ಸಿಂಕ್ ಮಾಡಿ.} OPT_TO_REG_OUTSIDE_LOCATION{ನಿಮ್ಮ ಕ್ಲೈಂಟ್ ಯಂತ್ರದ ಸ್ಥಳವು ನೋಂದಣಿ ಕೇಂದ್ರದ ಹೊರಗಿದೆ. ನೋಂದಣಿಯನ್ನು ನೋಂದಣಿ ಕೇಂದ್ರದೊಳಗೆ ಮಾತ್ರ ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ದಯವಿಟ್ಟು ಗಮನಿಸಿ} other{'Some error occurred!'}}", + "errors": "{messages, select, REG_TRY_AGAIN{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು!} REG_INVALID_REQUEST{ಪಾಸ್ ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ!} REG_MACHINE_NOT_FOUND{ಈ ಸಾಧನವನ್ನು ಇನ್ನೂ ಆನ್‌ಬೋರ್ಡ್ ಮಾಡಿಲ್ಲ. ಸಹಾಯಕ್ಕಾಗಿ ದಯವಿಟ್ಟು ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ!} REG_NETWORK_ERROR{ಲಾಗಿನ್ ವಿಫಲವಾಗಿದೆ. ನೆಟ್ವರ್ಕ್ ಸಂಪರ್ಕ ಪರಿಶೀಲಿಸಿ!} REG_CRED_EXPIRED{ರುಜುವಾತುಗಳು ಸಿಗಲಿಲ್ಲ ಅಥವಾ ಅವಧಿ ಮೀರಿದವು. ದಯವಿಟ್ಟು ಆನ್ ಲೈನ್ ಲಾಗಿನ್ ಪ್ರಯತ್ನಿಸಿ!} REG_MACHINE_INACTIVE{ಯಂತ್ರ ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_CENTER_INACTIVE{ಕೇಂದ್ರವು ಸಕ್ರಿಯವಾಗಿಲ್ಲ!} REG_LOGIN_LOCKED{ಗರಿಷ್ಠ ಲಾಗಿನ್ ಪ್ರಯತ್ನದ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ!} KER_SYN_AUTH_001{ದೃಢೀಕರಣ ಟೋಕನ್ ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ!} PAK_APPRVL_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಾಯಿತ ಪ್ಯಾಕೆಟ್‌ಗಳು ಅನುಮತಿಸಲಾದ ಅನುಮೋದನೆ ಸಮಯವನ್ನು ಮೀರಿದೆ. ಮುಂದುವರಿಸಲು ದಯವಿಟ್ಟು ಬ್ಯಾಕ್‌ಲಾಗ್ ಅನ್ನು ತೆರವುಗೊಳಿಸಿ.} PAK_UPLOAD_MAX_TIME{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಸರ್ವರ್‌ಗೆ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ ಅಥವಾ ರಫ್ತು ಮಾಡಿ.} PAK_UPLOAD_MAX_COUNT{ಕ್ರಮದ ಅಗತ್ಯವಿದೆ: ಪ್ಯಾಕೆಟ್ ಮಿತಿಯನ್ನು ತಲುಪಿದೆ. ಹೊಸ ನೋಂದಣಿಗಳನ್ನು ರಚಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ರಫ್ತು ಮಾಡಿ ಅಥವಾ ಅಪ್‌ಲೋಡ್ ಮಾಡಿ.} PAK_DISK_SPACE_LOW{ಪ್ಯಾಕೆಟ್ ರಚಿಸಲು ಅಗತ್ಯವಿರುವ ಕನಿಷ್ಟ ಸ್ಥಳಾವಕಾಶ ಲಭ್ಯವಿಲ್ಲ.} REG_PKT_APPRVL_CNT_EXCEED{ಕ್ಲೈಂಟ್‌ನಲ್ಲಿ ಅನುಮೋದನೆಗಾಗಿ ಬಾಕಿ ಉಳಿದಿರುವ ನೋಂದಣಿ ಪ್ಯಾಕೆಟ್‌ಗಳ ಗರಿಷ್ಠ ಸಂಖ್ಯೆಯನ್ನು ತಲುಪಿದೆ. ಈ ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಸುವ ಮೊದಲು ದಯವಿಟ್ಟು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಅನುಮೋದಿಸಿ ಅಥವಾ ತಿರಸ್ಕರಿಸಿ.} OPT_TO_REG_TIME_SYNC_EXCEED{ಕೊನೆಯ ಸಿಂಕ್‌ನಿಂದ ಸಮಯವು ಗರಿಷ್ಠ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಈ ನೋಂದಣಿಯೊಂದಿಗೆ ಮುಂದುವರಿಯುವ ಮೊದಲು ದಯವಿಟ್ಟು ಸರ್ವರ್‌ನಿಂದ ಸಿಂಕ್ ಮಾಡಿ.} OPT_TO_REG_OUTSIDE_LOCATION{ನಿಮ್ಮ ಕ್ಲೈಂಟ್ ಯಂತ್ರದ ಸ್ಥಳವು ನೋಂದಣಿ ಕೇಂದ್ರದ ಹೊರಗಿದೆ. ನೋಂದಣಿಯನ್ನು ನೋಂದಣಿ ಕೇಂದ್ರದೊಳಗೆ ಮಾತ್ರ ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ದಯವಿಟ್ಟು ಗಮನಿಸಿ} other{'Some error occurred!'}}", "@errors": { "description": "Error messages", "placeholders": {