Changeset a95bd1b
- Timestamp:
- 05/23/26 14:21:13 (4 months ago)
- Branches:
- master
- Children:
- 7e4a5fd
- Parents:
- 0e894d2
- Files:
-
- 3 added
- 10 edited
-
backend/src/main/java/medora/controller/BillingController.java (modified) (5 diffs)
-
backend/src/main/java/medora/dto/BillingDetailDTO.java (added)
-
backend/src/main/java/medora/dto/BillingItemDTO.java (added)
-
backend/src/main/java/medora/repository/BillingRepository.java (modified) (1 diff)
-
backend/src/main/java/medora/repository/PerformedLabTestRepository.java (modified) (2 diffs)
-
backend/src/main/java/medora/repository/PerformedProcedureRepository.java (modified) (1 diff)
-
backend/src/main/java/medora/service/AppointmentService.java (modified) (7 diffs)
-
backend/src/main/java/medora/service/BillingService.java (modified) (8 diffs)
-
backend/src/main/java/medora/service/LabService.java (modified) (7 diffs)
-
backend/src/main/java/medora/service/ProcedureService.java (modified) (5 diffs)
-
backend/src/main/java/medora/service/ReferralService.java (modified) (7 diffs)
-
backend/src/main/java/medora/util/BillingPDFGenerator.java (added)
-
pom.xml (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
backend/src/main/java/medora/controller/BillingController.java
r0e894d2 ra95bd1b 2 2 3 3 import medora.dto.BillingDTO; 4 import medora.dto.BillingDetailDTO; 4 5 import medora.dto.CreateBillingRequest; 5 6 import medora.dto.UpdateBillingRequest; 6 7 import medora.models.domain.Billing; 8 import medora.models.enums.PaymentStatus; 7 9 import medora.service.BillingService; 10 import medora.util.BillingPDFGenerator; 8 11 import org.slf4j.Logger; 9 12 import org.slf4j.LoggerFactory; 13 import org.springframework.http.HttpHeaders; 10 14 import org.springframework.http.HttpStatus; 15 import org.springframework.http.MediaType; 11 16 import org.springframework.http.ResponseEntity; 12 17 import org.springframework.web.bind.annotation.*; … … 82 87 } 83 88 89 @GetMapping("/{billId}/detail") 90 public ResponseEntity<?> getBillingDetail(@PathVariable Long billId) { 91 try { 92 logger.info("Fetching detailed billing information for bill ID: {}", billId); 93 BillingDetailDTO detail = billingService.getBillingDetail(billId); 94 return ResponseEntity.ok(detail); 95 } catch (RuntimeException e) { 96 logger.error("Error fetching billing detail: {}", e.getMessage()); 97 return ResponseEntity.badRequest() 98 .body(Map.of("error", e.getMessage())); 99 } catch (Exception e) { 100 logger.error("Unexpected error fetching billing detail: {}", e.getMessage()); 101 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) 102 .body(Map.of("error", "Failed to fetch billing detail: " + e.getMessage())); 103 } 104 } 105 84 106 @GetMapping 85 107 public ResponseEntity<?> getAllBillings() { … … 124 146 @PatchMapping("/{billId}/payment-status") 125 147 public ResponseEntity<?> updatePaymentStatus(@PathVariable Long billId, 126 @RequestBody UpdateBillingRequest request) {148 @RequestBody UpdateBillingRequest request) { 127 149 try { 128 150 if (request.getPaymentStatus() == null) { … … 144 166 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) 145 167 .body(Map.of("error", "Failed to update payment status: " + e.getMessage())); 168 } 169 } 170 171 @GetMapping("/{billId}/invoice-pdf") 172 public ResponseEntity<?> downloadInvoicePDF(@PathVariable Long billId) { 173 try { 174 logger.info("Generating PDF invoice for bill ID: {}", billId); 175 BillingDetailDTO billingDetail = billingService.getBillingDetail(billId); 176 byte[] pdfContent = BillingPDFGenerator.generateInvoicePDF(billingDetail); 177 178 HttpHeaders headers = new HttpHeaders(); 179 headers.setContentType(MediaType.APPLICATION_PDF); 180 headers.setContentDispositionFormData("attachment", "invoice-" + billId + ".pdf"); 181 headers.setContentLength(pdfContent.length); 182 183 return new ResponseEntity<>(pdfContent, headers, HttpStatus.OK); 184 } catch (RuntimeException e) { 185 logger.error("Error generating PDF invoice: {}", e.getMessage()); 186 return ResponseEntity.badRequest() 187 .body(Map.of("error", e.getMessage())); 188 } catch (Exception e) { 189 logger.error("Unexpected error generating PDF invoice: {}", e.getMessage()); 190 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) 191 .body(Map.of("error", "Failed to generate invoice: " + e.getMessage())); 146 192 } 147 193 } … … 151 197 if (billing.getMedicalRecord() != null && billing.getMedicalRecord().getPatient() != null) { 152 198 patientName = billing.getMedicalRecord().getPatient().getFirstName() + " " + 153 billing.getMedicalRecord().getPatient().getLastName();199 billing.getMedicalRecord().getPatient().getLastName(); 154 200 } 155 201 return new BillingDTO( -
backend/src/main/java/medora/repository/BillingRepository.java
r0e894d2 ra95bd1b 79 79 """) 80 80 List<Billing> findBillingHistoryForPatient(@Param("patientId") Long patientId); 81 82 // UC020 – Auto billing: Check if billing exists for patient on a specific date 83 @Query(value = """ 84 SELECT COUNT(b.bill_id) > 0 85 FROM billing b 86 JOIN medical_records mr ON b.record_id = mr.record_id 87 WHERE mr.patient_id = :patientId 88 AND CAST(b.payment_date AS DATE) = :billDate 89 """, nativeQuery = true) 90 boolean existsBillingForPatientOnDate(@Param("patientId") Long patientId, @Param("billDate") java.time.LocalDate billDate); 91 92 // UC020 – Auto billing: Get billing for patient on a specific date 93 @Query(value = """ 94 SELECT b.* FROM billing b 95 JOIN medical_records mr ON b.record_id = mr.record_id 96 WHERE mr.patient_id = :patientId 97 AND CAST(b.payment_date AS DATE) = :billDate 98 LIMIT 1 99 """, nativeQuery = true) 100 Billing findBillingForPatientOnDate(@Param("patientId") Long patientId, @Param("billDate") java.time.LocalDate billDate); 101 102 // UC020 – Auto billing: Calculate total cost for patient on a specific date (using view) 103 @Query(value = """ 104 SELECT COALESCE(total_cost, 0::decimal) 105 FROM daily_patient_billing_totals 106 WHERE patient_id = :patientId 107 AND service_date = :serviceDate 108 """, nativeQuery = true) 109 BigDecimal calculateDailyTotalCostForPatient(@Param("patientId") Long patientId, @Param("serviceDate") java.time.LocalDate serviceDate); 110 111 // UC020 – Get procedures for a billing record 112 @Query(value = """ 113 SELECT p.procedure_id, p.procedure_type, p.cost 114 FROM billing_procedures bp 115 JOIN procedures p ON bp.procedure_id = p.procedure_id 116 WHERE bp.bill_id = :billId 117 """, nativeQuery = true) 118 java.util.List<Object[]> findProceduresForBilling(@Param("billId") Long billId); 119 120 // UC020 – Get lab tests for a billing record 121 @Query(value = """ 122 SELECT l.test_id, l.test_name, l.cost 123 FROM billing_lab_tests blt 124 JOIN lab_tests l ON blt.test_id = l.test_id 125 WHERE blt.bill_id = :billId 126 """, nativeQuery = true) 127 java.util.List<Object[]> findLabTestsForBilling(@Param("billId") Long billId); 81 128 } -
backend/src/main/java/medora/repository/PerformedLabTestRepository.java
r0e894d2 ra95bd1b 4 4 import medora.models.domain.PerformedLabTests; 5 5 import org.springframework.data.jpa.repository.JpaRepository; 6 import org.springframework.data.jpa.repository.Query; 7 import org.springframework.data.repository.query.Param; 6 8 7 9 import java.util.List; … … 12 14 13 15 List<PerformedLabTests> findByDoctorDoctorId(Long doctorId); 16 17 // UC020 – Auto billing: Get lab tests for a patient on a specific date 18 @Query(""" 19 SELECT plt FROM PerformedLabTests plt 20 WHERE plt.patient.patientId = :patientId 21 AND plt.testDate = :testDate 22 """) 23 List<PerformedLabTests> findByPatientAndDate(@Param("patientId") Long patientId, @Param("testDate") java.time.LocalDate testDate); 14 24 } -
backend/src/main/java/medora/repository/PerformedProcedureRepository.java
r0e894d2 ra95bd1b 45 45 """) 46 46 List<PerformedProcedures> findByMedicalRecordId(@Param("recordId") Long recordId); 47 48 // UC020 – Auto billing: Get procedures for a patient on a specific date 49 @Query(""" 50 SELECT pp FROM PerformedProcedures pp 51 WHERE pp.patient.patientId = :patientId 52 AND pp.procedureDate = :procedureDate 53 """) 54 List<PerformedProcedures> findByPatientAndDate(@Param("patientId") Long patientId, @Param("procedureDate") java.time.LocalDate procedureDate); 47 55 } -
backend/src/main/java/medora/service/AppointmentService.java
r0e894d2 ra95bd1b 2 2 3 3 import medora.models.domain.Appointment; 4 import medora.models.domain.Patient; 4 5 import medora.models.domain.Doctors; 5 import medora.models.domain.Patient;6 6 import medora.models.enums.AppointmentStatus; 7 7 import medora.repository.AppointmentRepository; 8 import medora.repository.PatientRepository; 8 9 import medora.repository.DoctorRepository; 9 import medora.repository.PatientRepository; 10 10 11 import org.slf4j.Logger; 11 12 import org.slf4j.LoggerFactory; 13 12 14 import org.springframework.stereotype.Service; 13 15 import org.springframework.transaction.annotation.Transactional; … … 79 81 LocalDateTime.of(appointmentDate, appointmentTime); 80 82 81 83 // Future validation 82 84 if (!appointmentDateTime.isAfter(LocalDateTime.now())) { 83 85 throw new RuntimeException( … … 86 88 } 87 89 90 // Doctor slot validation 88 91 boolean doctorBusy = 89 92 appointmentRepository … … 99 102 } 100 103 104 // Duplicate patient validation 101 105 boolean duplicateAppointment = 102 106 appointmentRepository … … 204 208 } 205 209 206 207 210 @Transactional(readOnly = true) 208 211 public Optional<Appointment> getAppointmentById(Long appointmentId) { … … 217 220 } 218 221 219 /** 220 * Get appointments for patient 221 */ 222 222 223 @Transactional(readOnly = true) 223 224 public List<Appointment> getAppointmentsForPatient(Long patientId) { … … 297 298 return appointmentRepository.findAll(); 298 299 } 300 301 302 public LocalTime findNextAvailableSlot(Long doctorId, LocalDate appointmentDate) { 303 LocalTime[] timeSlots = { 304 LocalTime.of(9, 0), 305 LocalTime.of(10, 0), 306 LocalTime.of(11, 0), 307 LocalTime.of(13, 0), 308 LocalTime.of(14, 0), 309 LocalTime.of(15, 0), 310 LocalTime.of(16, 0) 311 }; 312 313 for (LocalTime timeSlot : timeSlots) { 314 boolean isBooked = appointmentRepository 315 .existsByDoctorDoctorIdAndAppointmentDateAndAppointmentTimeAndStatusNot( 316 doctorId, 317 appointmentDate, 318 timeSlot, 319 AppointmentStatus.CANCELLED 320 ); 321 if (!isBooked) { 322 return timeSlot; 323 } 324 } 325 return null; 326 } 299 327 } -
backend/src/main/java/medora/service/BillingService.java
r0e894d2 ra95bd1b 1 1 package medora.service; 2 2 3 import medora.models.domain.*; 3 import medora.models.domain.Billing; 4 import medora.models.domain.MedicalRecord; 5 import medora.models.domain.Admin; 6 import medora.models.domain.BillingLabTests; 7 import medora.models.domain.BillingProcedures; 8 import medora.models.domain.PerformedProcedures; 9 import medora.models.domain.PerformedLabTests; 4 10 import medora.models.enums.PaymentStatus; 5 import medora.repository.*; 11 import medora.repository.BillingRepository; 12 import medora.repository.MedicalRecordRepository; 13 import medora.repository.AdminRepository; 14 import medora.repository.BillingLabTestsRepository; 15 import medora.repository.BillingProceduresRepository; 16 import medora.repository.PerformedProcedureRepository; 17 import medora.repository.PerformedLabTestRepository; 18 import medora.repository.PatientRepository; 19 import medora.dto.BillingDetailDTO; 20 import medora.dto.BillingItemDTO; 6 21 import org.slf4j.Logger; 7 22 import org.slf4j.LoggerFactory; … … 13 28 import java.util.List; 14 29 import java.util.Optional; 30 import java.util.ArrayList; 15 31 16 32 /** … … 30 46 private final BillingLabTestsRepository billingLabTestsRepository; 31 47 private final BillingProceduresRepository billingProceduresRepository; 48 private final PerformedProcedureRepository performedProcedureRepository; 49 private final PerformedLabTestRepository performedLabTestRepository; 50 private final PatientRepository patientRepository; 32 51 33 52 public BillingService(BillingRepository billingRepository, … … 35 54 AdminRepository adminRepository, 36 55 BillingLabTestsRepository billingLabTestsRepository, 37 BillingProceduresRepository billingProceduresRepository) { 56 BillingProceduresRepository billingProceduresRepository, 57 PerformedProcedureRepository performedProcedureRepository, 58 PerformedLabTestRepository performedLabTestRepository, 59 PatientRepository patientRepository) { 38 60 this.billingRepository = billingRepository; 39 61 this.medicalRecordRepository = medicalRecordRepository; … … 41 63 this.billingLabTestsRepository = billingLabTestsRepository; 42 64 this.billingProceduresRepository = billingProceduresRepository; 65 this.performedProcedureRepository = performedProcedureRepository; 66 this.performedLabTestRepository = performedLabTestRepository; 67 this.patientRepository = patientRepository; 43 68 } 44 69 … … 196 221 } 197 222 198 logger.info("Total billing cost for bill ID {}: procedures={}, lab tests={}", 223 logger.info("Total billing cost for bill ID {}: procedures={}, lab tests={}", 199 224 billId, procedureCost, labTestCost); 200 225 return procedureCost.add(labTestCost); … … 219 244 // This is a placeholder - adjust based on your actual Procedure entity 220 245 logger.info("Adding procedure {} to billing record {}", procedureId, billId); 221 246 222 247 return null; // Will be implemented with ProcedureRepository injection 223 248 } … … 241 266 // This is a placeholder - adjust based on your actual LabTests entity 242 267 logger.info("Adding lab test {} to billing record {}", testId, billId); 243 268 244 269 return null; // Will be implemented with LabTestRepository injection 245 270 } 271 272 /** 273 * UC020 – Auto-generate billing when a procedure or lab test is performed 274 * Creates a billing record if one doesn't exist for the patient on that date 275 * Calculates total cost from all procedures and lab tests performed that day 276 */ 277 @Transactional 278 public void autoGenerateBillingForPatientService(Long patientId, LocalDate serviceDate) { 279 try { 280 if (patientId == null || patientId <= 0) { 281 throw new IllegalArgumentException("Patient ID must be valid"); 282 } 283 if (serviceDate == null) { 284 throw new IllegalArgumentException("Service date must be valid"); 285 } 286 287 logger.info("Starting auto-billing for patient {} on date {}", patientId, serviceDate); 288 289 // Get patient's medical record (or create one if it doesn't exist) 290 MedicalRecord medicalRecord = medicalRecordRepository.findByPatientPatientId(patientId) 291 .orElseGet(() -> { 292 logger.info("Creating new medical record for patient {}", patientId); 293 MedicalRecord newRecord = new MedicalRecord(); 294 newRecord.setPatient(patientRepository.findById(patientId) 295 .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + patientId))); 296 return medicalRecordRepository.save(newRecord); 297 }); 298 299 logger.info("Using medical record {} for patient {}", medicalRecord.getRecordId(), patientId); 300 301 // Get all procedures and lab tests for the patient on that date 302 List<PerformedProcedures> procedures = performedProcedureRepository.findByPatientAndDate(patientId, serviceDate); 303 List<PerformedLabTests> labTests = performedLabTestRepository.findByPatientAndDate(patientId, serviceDate); 304 305 logger.info("Found {} procedures and {} lab tests for patient {} on {}", 306 procedures.size(), labTests.size(), patientId, serviceDate); 307 308 // Only generate billing if there are procedures or lab tests on that date 309 if (procedures.isEmpty() && labTests.isEmpty()) { 310 logger.info("No procedures or lab tests found for patient {} on {}", patientId, serviceDate); 311 return; 312 } 313 314 // Calculate total cost 315 BigDecimal procedureCost = procedures.stream() 316 .map(p -> { 317 BigDecimal cost = p.getProcedure().getCost(); 318 logger.debug("Procedure {} cost: {}", p.getProcedure().getProcedureId(), cost); 319 return cost; 320 }) 321 .reduce(BigDecimal.ZERO, BigDecimal::add); 322 323 BigDecimal labTestCost = labTests.stream() 324 .map(lt -> { 325 BigDecimal cost = lt.getLabTest().getCost(); 326 logger.debug("Lab test {} cost: {}", lt.getLabTest().getTestId(), cost); 327 return cost; 328 }) 329 .reduce(BigDecimal.ZERO, BigDecimal::add); 330 331 BigDecimal totalCost = procedureCost.add(labTestCost); 332 logger.info("Total cost calculation: procedures={}, labTests={}, total={}", procedureCost, labTestCost, totalCost); 333 334 // Check if billing already exists for this patient on this date 335 Billing billing = billingRepository.findBillingForPatientOnDate(patientId, serviceDate); 336 337 if (billing != null) { 338 logger.info("Billing record {} already exists for patient {} on {}, updating with new total", 339 billing.getBillId(), patientId, serviceDate); 340 billing.setTotalCost(totalCost); 341 } else { 342 // Get default admin (first admin in system) 343 Admin admin = adminRepository.findAll() 344 .stream() 345 .findFirst() 346 .orElseThrow(() -> new RuntimeException("No admin found in system")); 347 348 // Create new billing record 349 billing = new Billing(); 350 billing.setMedicalRecord(medicalRecord); 351 billing.setAdmin(admin); 352 billing.setTotalCost(totalCost); 353 billing.setPaymentStatus(PaymentStatus.PENDING); 354 billing.setPaymentDate(serviceDate); 355 356 logger.info("Creating new billing record for patient {} on {}", patientId, serviceDate); 357 } 358 359 Billing savedBilling = billingRepository.save(billing); 360 logger.info("Billing record {} for patient {} on {} with total cost: {}", 361 savedBilling.getBillId(), patientId, serviceDate, totalCost); 362 363 // Link procedures to billing (only if not already linked) 364 for (PerformedProcedures procedure : procedures) { 365 try { 366 BillingProcedures billingProcedure = new BillingProcedures(savedBilling, procedure.getProcedure()); 367 billingProceduresRepository.save(billingProcedure); 368 logger.debug("Linked procedure {} to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId()); 369 } catch (Exception e) { 370 logger.debug("Procedure {} already linked to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId()); 371 } 372 } 373 374 // Link lab tests to billing (only if not already linked) 375 for (PerformedLabTests labTest : labTests) { 376 try { 377 BillingLabTests billingLabTest = new BillingLabTests(savedBilling, labTest.getLabTest()); 378 billingLabTestsRepository.save(billingLabTest); 379 logger.debug("Linked lab test {} to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId()); 380 } catch (Exception e) { 381 logger.debug("Lab test {} already linked to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId()); 382 } 383 } 384 385 logger.info("Successfully processed {} procedures and {} lab tests for billing record {}", 386 procedures.size(), labTests.size(), savedBilling.getBillId()); 387 388 } catch (Exception e) { 389 logger.error("Error in auto-billing for patient {} on date {}: {}", patientId, serviceDate, e.getMessage(), e); 390 throw e; 391 } 392 } 393 394 /** 395 * Get detailed billing information with itemized procedures and lab tests 396 */ 397 @Transactional(readOnly = true) 398 public BillingDetailDTO getBillingDetail(Long billId) { 399 if (billId == null || billId <= 0) { 400 throw new IllegalArgumentException("Bill ID must be valid"); 401 } 402 403 Billing billing = billingRepository.findById(billId) 404 .orElseThrow(() -> new RuntimeException("Billing record not found with ID: " + billId)); 405 406 // Get procedures for this bill 407 List<Object[]> procedureResults = billingRepository.findProceduresForBilling(billId); 408 List<BillingItemDTO> procedures = new ArrayList<>(); 409 for (Object[] row : procedureResults) { 410 procedures.add(new BillingItemDTO( 411 ((Number) row[0]).longValue(), 412 (String) row[1], 413 (BigDecimal) row[2] 414 )); 415 } 416 417 // Get lab tests for this bill 418 List<Object[]> labTestResults = billingRepository.findLabTestsForBilling(billId); 419 List<BillingItemDTO> labTests = new ArrayList<>(); 420 for (Object[] row : labTestResults) { 421 labTests.add(new BillingItemDTO( 422 ((Number) row[0]).longValue(), 423 (String) row[1], 424 (BigDecimal) row[2] 425 )); 426 } 427 428 // Build the detail DTO 429 BillingDetailDTO detail = new BillingDetailDTO(); 430 detail.setBillId(billing.getBillId()); 431 detail.setPatientName(billing.getMedicalRecord().getPatient().getFirstName() + " " + 432 billing.getMedicalRecord().getPatient().getLastName()); 433 detail.setPatientEmbg(billing.getMedicalRecord().getPatient().getEmbg()); 434 detail.setPatientPhone(billing.getMedicalRecord().getPatient().getPhoneNumber()); 435 detail.setTotalCost(billing.getTotalCost()); 436 detail.setPaymentStatus(billing.getPaymentStatus().toString()); 437 detail.setPaymentDate(billing.getPaymentDate()); 438 detail.setBillDate(billing.getPaymentDate()); 439 detail.setProcedures(procedures); 440 detail.setLabTests(labTests); 441 442 logger.info("Retrieved detailed billing information for bill {}", billId); 443 return detail; 444 } 246 445 } -
backend/src/main/java/medora/service/LabService.java
r0e894d2 ra95bd1b 31 31 private final DoctorRepository doctorRepository; 32 32 private final LabTechnicianRepository labTechnicianRepository; 33 private final BillingService billingService; 33 34 34 35 public LabService(LabTestRepository labTestRepository, … … 39 40 PatientRepository patientRepository, 40 41 DoctorRepository doctorRepository, 41 LabTechnicianRepository labTechnicianRepository) { 42 LabTechnicianRepository labTechnicianRepository, 43 BillingService billingService) { 42 44 this.labTestRepository = labTestRepository; 43 45 this.labResultsRepository = labResultsRepository; … … 48 50 this.doctorRepository = doctorRepository; 49 51 this.labTechnicianRepository = labTechnicianRepository; 50 } 51 52 // LAB TEST 52 this.billingService = billingService; 53 } 54 55 // ================= LAB TEST ================= 53 56 54 57 @Transactional … … 104 107 } 105 108 106 // LAB TEST REQUESTS (UC013)109 // ================= LAB TEST REQUESTS (UC013) ================= 107 110 108 111 @Transactional … … 134 137 performedTest.setDoctor(doctor); 135 138 performedTest.setLabTest(test); 136 performedTest.setTestDate(testDate != null ? testDate : LocalDate.now()); 139 LocalDate finalTestDate = testDate != null ? testDate : LocalDate.now(); 140 performedTest.setTestDate(finalTestDate); 137 141 performedTest.setNotes(notes); 138 142 139 143 logger.info("Lab test {} requested for patient {} by doctor {}", testId, patientId, doctorId); 140 return performedLabTestRepository.save(performedTest); 144 PerformedLabTests saved = performedLabTestRepository.save(performedTest); 145 146 // Auto-generate billing for the patient on this date 147 billingService.autoGenerateBillingForPatientService(patientId, finalTestDate); 148 149 return saved; 141 150 } 142 151 … … 163 172 } 164 173 165 // LAB RESULTS (UC014, UC015)174 // ================= LAB RESULTS (UC014, UC015) ================= 166 175 167 176 @Transactional … … 193 202 LabResults saved = labResultsRepository.save(labResult); 194 203 195 204 // Use the join repository to safely link (avoid deleting existing links) 205 // Create and save the join entity explicitly 196 206 MedicalRecordLabResults link = new MedicalRecordLabResults(); 197 207 link.setMedicalRecord(medicalRecord); -
backend/src/main/java/medora/service/ProcedureService.java
r0e894d2 ra95bd1b 29 29 private final MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository; 30 30 private final EntityManager entityManager; 31 private final BillingService billingService; 31 32 32 33 public ProcedureService(PerformedProcedureRepository performedProcedureRepository, … … 39 40 ProcedureResultRepository procedureResultRepository, 40 41 MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository, 41 EntityManager entityManager) { 42 EntityManager entityManager, 43 BillingService billingService) { 42 44 43 45 this.performedProcedureRepository = performedProcedureRepository; … … 51 53 this.medicalRecordProcedureResultRepository = medicalRecordProcedureResultRepository; 52 54 this.entityManager = entityManager; 55 this.billingService = billingService; 53 56 } 54 57 … … 97 100 98 101 logger.info("Requested procedure {} for patient {} by doctor {}", procedureId, patientId, doctorId); 99 return performedProcedureRepository.saveAndFlush(performed); 102 PerformedProcedures saved = performedProcedureRepository.saveAndFlush(performed); 103 104 // Auto-generate billing for the patient on this date 105 billingService.autoGenerateBillingForPatientService(patientId, procedureDate); 106 107 return saved; 100 108 } 101 109 … … 142 150 143 151 logger.info("Recorded procedure {} for patient {}", procedureId, patientId); 144 return performedProcedureRepository.saveAndFlush(performed); 152 PerformedProcedures saved = performedProcedureRepository.saveAndFlush(performed); 153 154 // Auto-generate billing for the patient on this date 155 billingService.autoGenerateBillingForPatientService(patientId, procedureDate); 156 157 return saved; 145 158 } 146 159 -
backend/src/main/java/medora/service/ReferralService.java
r0e894d2 ra95bd1b 1 1 package medora.service; 2 2 3 import medora.models.domain.Referrals; 3 4 import medora.models.domain.Doctors; 4 import medora. models.domain.Referrals;5 import medora.repository.ReferralRepository; 5 6 import medora.repository.DoctorRepository; 7 import medora.repository.PatientRepository; 6 8 import medora.repository.MedicalRecordRepository; 7 import medora.repository.PatientRepository;8 import medora.repository.ReferralRepository;9 9 import org.slf4j.Logger; 10 10 import org.slf4j.LoggerFactory; … … 13 13 14 14 import java.time.LocalDate; 15 import java.time.LocalTime; 15 16 import java.util.List; 16 17 import java.util.Optional; … … 19 20 * RefferalService handles referral operations. 20 21 * UC019 – Create Referral Record 21 * OPTIONAL - Use only if needed 22 22 23 */ 23 24 @Service … … 30 31 private final PatientRepository patientRepository; 31 32 private final MedicalRecordRepository medicalRecordRepository; 33 private final AppointmentService appointmentService; 32 34 33 35 public ReferralService(ReferralRepository referralRepository, 34 36 DoctorRepository doctorRepository, 35 37 PatientRepository patientRepository, 36 MedicalRecordRepository medicalRecordRepository) { 38 MedicalRecordRepository medicalRecordRepository, 39 AppointmentService appointmentService) { 37 40 this.referralRepository = referralRepository; 38 41 this.doctorRepository = doctorRepository; 39 42 this.patientRepository = patientRepository; 40 43 this.medicalRecordRepository = medicalRecordRepository; 44 this.appointmentService = appointmentService; 41 45 } 42 46 43 47 /** 44 48 * UC019 – Create Referral Record 45 * Create a referral from one doctor to another 49 * Create a referral from one doctor to another and automatically create an appointment 46 50 */ 47 51 @Transactional 48 52 public Referrals createReferral(Long medicalRecordId, Long fromDoctorId, Long toDoctorId, 49 String reason, LocalDate referralDate ) {53 String reason, LocalDate referralDate, LocalDate appointmentDate, LocalTime appointmentTime) { 50 54 if (medicalRecordId == null || medicalRecordId <= 0) { 51 55 throw new IllegalArgumentException("Medical record ID must be valid"); … … 62 66 if (referralDate == null) { 63 67 throw new IllegalArgumentException("Referral date is required"); 68 } 69 if (appointmentDate == null) { 70 throw new IllegalArgumentException("Appointment date is required"); 71 } 72 if (appointmentTime == null) { 73 throw new IllegalArgumentException("Appointment time is required"); 64 74 } 65 75 … … 83 93 referral.setReason(reason); 84 94 referral.setReferralDate(referralDate); 95 referral.setAppointmentDate(appointmentDate); 96 referral.setAppointmentTime(appointmentTime); 85 97 86 98 logger.info("Creating referral for medical record ID: {} from doctor ID: {} to doctor ID: {}", 87 99 medicalRecordId, fromDoctorId, toDoctorId); 88 return referralRepository.save(referral); 100 Referrals savedReferral = referralRepository.save(referral); 101 102 return savedReferral; 89 103 } 90 104 … … 143 157 return referralRepository.findIncomingReferralsForDoctor(doctorId); 144 158 } 159 160 161 @Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW) 162 private void createAppointmentForReferral(Long patientId, Long doctorId, LocalDate appointmentDate, LocalTime appointmentTime) { 163 try { 164 appointmentService.createAppointment(patientId, doctorId, appointmentDate, appointmentTime); 165 logger.info("Automatically created appointment for patient ID: {} with doctor ID: {} on {} at {}", 166 patientId, doctorId, appointmentDate, appointmentTime); 167 } catch (RuntimeException e) { 168 logger.warn("Failed to create appointment for referral. Error: {}", e.getMessage()); 169 } 170 } 145 171 } -
pom.xml
r0e894d2 ra95bd1b 10 10 </parent> 11 11 <groupId>medora</groupId> 12 <artifactId>medora 5</artifactId>12 <artifactId>medora4</artifactId> 13 13 <version>0.0.1-SNAPSHOT</version> 14 <name>medora 5</name>15 <description>medora 5</description>14 <name>medora4</name> 15 <description>medora4</description> 16 16 <url/> 17 17 <licenses> … … 75 75 <artifactId>spring-boot-starter-validation</artifactId> 76 76 </dependency> 77 78 <!-- iText for PDF generation --> 79 <dependency> 80 <groupId>com.itextpdf</groupId> 81 <artifactId>kernel</artifactId> 82 <version>7.2.5</version> 83 </dependency> 84 <dependency> 85 <groupId>com.itextpdf</groupId> 86 <artifactId>layout</artifactId> 87 <version>7.2.5</version> 88 </dependency> 89 <dependency> 90 <groupId>com.itextpdf</groupId> 91 <artifactId>io</artifactId> 92 <version>7.2.5</version> 93 </dependency> 77 94 </dependencies> 78 95
Note:
See TracChangeset
for help on using the changeset viewer.
