diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 38be2dce..05a361a2 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -6,10 +6,10 @@ spring.datasource.password=${DATABASE_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ## S3 storage for diagnostic documents pushed from a van -aws.s3.access-key=${AWS_S3_ACCESS_KEY} -aws.s3.secret-key=${AWS_S3_SECRET_KEY} -aws.s3.region=${AWS_S3_REGION} -diagnostic.documents.s3.bucket=${DIAGNOSTIC_DOCUMENTS_S3_BUCKET} +aws.s3.access-key=${AWS_S3_ACCESS_KEY:} +aws.s3.secret-key=${AWS_S3_SECRET_KEY:} +aws.s3.region=${AWS_S3_REGION:ap-south-1} +diagnostic.documents.s3.bucket=${DIAGNOSTIC_DOCUMENTS_S3_BUCKET:} ## Carestream URLs, local carestreamOrderCreateURL=${COMMON_API}carestream/createOrder @@ -36,7 +36,7 @@ dataSyncDownloadUrl=${MMU_CENTRAL_SERVER}dataSync/server-to-van ## Diagnostic document push (this server -> further central server) diagnosticDocumentUploadUrl=${MMU_CENTRAL_SERVER}dataSync/diagnostic-documents -diagnosticDocument.push.batchSize=${DIAGNOSTIC_DOCUMENT_PUSH_BATCH_SIZE} +diagnosticDocument.push.batchSize=${DIAGNOSTIC_DOCUMENT_PUSH_BATCH_SIZE:3} diagnostic.documents.storage-root=${DIAGNOSTIC_DOCUMENTS_STORAGE_ROOT} ## TC specialist slot booking, # not required diff --git a/src/main/java/com/iemr/mmu/config/S3ClientConfig.java b/src/main/java/com/iemr/mmu/config/S3ClientConfig.java index af02dcbb..eb3509bf 100644 --- a/src/main/java/com/iemr/mmu/config/S3ClientConfig.java +++ b/src/main/java/com/iemr/mmu/config/S3ClientConfig.java @@ -5,6 +5,8 @@ import org.springframework.context.annotation.Configuration; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; @@ -26,7 +28,16 @@ public class S3ClientConfig { @Value("${aws.s3.region}") private String region; - private StaticCredentialsProvider credentialsProvider() { + /* + * Only the central server talks to S3; a van (camp laptop) runs the same war but never + * calls it, so it must be able to start without AWS keys. AwsBasicCredentials rejects blank + * keys at construction time, so fall back to the SDK default chain, which is resolved lazily + * on the first S3 call rather than at startup. + */ + private AwsCredentialsProvider credentialsProvider() { + if (accessKey == null || accessKey.isBlank() || secretKey == null || secretKey.isBlank()) { + return DefaultCredentialsProvider.create(); + } return StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)); } diff --git a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DataSyncRepository.java b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DataSyncRepository.java index f4850923..99b4ec04 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DataSyncRepository.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DataSyncRepository.java @@ -169,6 +169,64 @@ public List> getSyncFailures() { // ---------------------------------- End of Upload repository // ---------------------------------- Download Repository + + /** + * Down-sync copies the beneficiary tables but not i_ben_flow_outreach, and the tab download + * (FLW-API getBeneficiaryData) only serves beneficiaries that have a worklist row. So a + * beneficiary registered elsewhere and down-synced here never reached this laptop's tabs. + * Creates the missing worklist row for this van's beneficiaries, filled as TM-API does at + * registration. Processed = 'P' so it is not uploaded back: central already has its own row. + */ + public int createMissingWorklistRows(int vanID, int providerServiceMapID) { + jdbcTemplate = getJdbcTemplate(); + + String village = "(SELECT h.%s FROM db_identity.i_beneficiarydetails_rmnch r " + + " JOIN db_identity.i_householddetails h ON h.houseoldId = r.houseoldId " + + " WHERE r.BeneficiaryRegID = bm.BenRegId LIMIT 1)"; + + String insert = "INSERT INTO db_iemr.i_ben_flow_outreach " + + " (beneficiary_reg_id, beneficiary_id, visit_no, ben_name, ben_age, ben_dob, ben_gender, " + + " ben_gender_val, ben_age_val, ben_phone_no, father_name, spouse_name, nurse_flag, doctor_flag, " + + " pharmacist_flag, doctor_signature_flag, BenArrivedFlag, created_by, created_date, modified_by, " + + " modified_date, registrationDate, districtID, district, villageID, village, " + + " providerServiceMapID, vanID, Processed, deleted) " + + " SELECT * FROM ( SELECT bm.BenRegId, rg.BeneficiaryID, 1 AS visit_no, " + + " TRIM(CONCAT_WS(' ', d.FirstName, NULLIF(d.MiddleName, ''), d.LastName)) AS ben_name, " + + " CONCAT(TIMESTAMPDIFF(YEAR, d.DOB, bm.CreatedDate), ' years - ', " + + " MOD(TIMESTAMPDIFF(MONTH, d.DOB, bm.CreatedDate), 12), ' months') AS ben_age, " + + " d.DOB, d.Gender, d.GenderId, TIMESTAMPDIFF(YEAR, d.DOB, bm.CreatedDate) AS ben_age_val, " + + " COALESCE(c.PreferredPhoneNum, c.PhoneNum1) AS phone, " + + " COALESCE(d.FatherName, '') AS father, COALESCE(d.SpouseName, '') AS spouse, " + + " 1 AS nf, 0 AS df, 0 AS pf, 0 AS dsf, 0 AS baf, bm.CreatedBy, bm.CreatedDate AS cdate, " + + " 'downsync' AS mby, NOW() AS mdate, bm.CreatedDate AS rdate, " + + " COALESCE(a.CurrDistrictId, a.PermDistrictId) AS districtID, " + + " COALESCE(a.CurrDistrict, a.PermDistrict) AS district, " + + " COALESCE(" + String.format(village, "villageid") + ", a.CurrVillageId, a.PermVillageId) AS villageID, " + + " COALESCE(" + String.format(village, "village") + ", a.CurrVillage, a.PermVillage) AS village, " + + " ? AS psm, bm.VanID, 'P' AS processed, 0 AS deleted " + + " FROM db_identity.i_beneficiarymapping bm " + + " JOIN db_identity.m_beneficiaryregidmapping rg ON rg.BenRegId = bm.BenRegId " + + " JOIN db_identity.i_beneficiarydetails d ON d.BeneficiaryDetailsId = bm.BenDetailsId " + + " LEFT JOIN db_identity.i_beneficiarycontacts c ON c.BenContactsID = bm.BenContactsId " + + " LEFT JOIN db_identity.i_beneficiaryaddress a ON a.BenAddressID = bm.BenAddressId " + + " WHERE bm.VanID = ? AND bm.Deleted = false " + // the van can hold beneficiaries of other service lines; only its own get a row + + " AND (SELECT r.ProviderServiceMapID FROM db_identity.i_beneficiarydetails_rmnch r " + + " WHERE r.BeneficiaryRegID = bm.BenRegId LIMIT 1) = ? " + + " AND NOT EXISTS (SELECT 1 FROM db_iemr.i_ben_flow_outreach f " + + " WHERE f.beneficiary_reg_id = bm.BenRegId) ) x " + + " WHERE x.villageID IS NOT NULL"; + + int inserted = jdbcTemplate.update(insert, providerServiceMapID, vanID, providerServiceMapID); + + if (inserted > 0) + // same convention as TM-API: VanSerialNo is the row's own id + jdbcTemplate.update("UPDATE db_iemr.i_ben_flow_outreach SET VanSerialNo = ben_flow_id " + + " WHERE modified_by = 'downsync' AND VanSerialNo IS NULL"); + + return inserted; + } + public int[] updateLatestMasterInLocal(String query, List syncDataList) { int[] i = null; // get JDBC template diff --git a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownSyncDataFromServerImpl.java b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownSyncDataFromServerImpl.java index c3c1e1d0..2c70854e 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownSyncDataFromServerImpl.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownSyncDataFromServerImpl.java @@ -163,6 +163,7 @@ public String startDownSync(String serverAuthorization, String jwtToken, Integer } progressCounter++; } + createMissingWorklistRows(vanID, providerServiceMapID); countOutstandingConflicts(downSyncTables, vanID); } finally { IN_PROGRESS.set(false); @@ -207,6 +208,23 @@ public Map getDownSyncStatus() { return resultMap; } + /** Down-synced beneficiaries need a worklist row before this laptop's tabs can download them. */ + private void createMissingWorklistRows(Integer vanID, Integer providerServiceMapID) { + try { + Integer psm = providerServiceMapID != null ? providerServiceMapID + : dataSyncRepository.getVanProviderServiceMapID(vanID); + if (psm == null) { + logger.warn("Worklist rows not created after down-sync: no service line for van {}", vanID); + return; + } + int created = dataSyncRepository.createMissingWorklistRows(vanID, psm); + logger.info("Created {} worklist rows for down-synced beneficiaries of van {}", created, vanID); + } catch (Exception e) { + // the down-sync itself already succeeded; a failure here must not undo it + logger.error("Creating worklist rows after down-sync failed for van " + vanID + " : " + e.getMessage(), e); + } + } + private void countOutstandingConflicts(List downSyncTables, Integer vanID) { outstandingConflictCounter = 0; outstandingConflictTables.clear();