Changeset 48d2bed for backend/src/main/java
- Timestamp:
- 09/24/26 12:09:29 (2 days ago)
- Branches:
- master
- Parents:
- ae6cd79
- Location:
- backend/src/main/java/medora
- Files:
-
- 2 added
- 21 deleted
- 56 edited
-
controller/DoctorController.java (modified) (2 diffs)
-
controller/LabController.java (modified) (2 diffs)
-
controller/LabTechnicianController.java (added)
-
controller/PatientController.java (modified) (2 diffs)
-
controller/ReferralController.java (modified) (4 diffs)
-
dto/CreateDoctorRequest.java (modified) (1 diff)
-
dto/CreatePatientRequest.java (modified) (1 diff)
-
dto/LabTechnicianDTO.java (added)
-
dto/RequestLabTestRequest.java (modified) (1 diff)
-
exception/DiagnosisProcedureNotFoundException.java (deleted)
-
exception/DoctorMedicalRecordNotFoundException.java (deleted)
-
models/domain/Admin.java (modified) (1 diff)
-
models/domain/Appointment.java (modified) (1 diff)
-
models/domain/Billing.java (modified) (2 diffs)
-
models/domain/Diagnosis.java (modified) (1 diff)
-
models/domain/DiagnosisProcedure.java (deleted)
-
models/domain/DoctorMedicalRecord.java (deleted)
-
models/domain/Doctors.java (modified) (3 diffs)
-
models/domain/LabResults.java (modified) (1 diff)
-
models/domain/LabTechnician.java (modified) (1 diff)
-
models/domain/LabTests.java (modified) (1 diff)
-
models/domain/MedicalRecord.java (modified) (1 diff)
-
models/domain/MedicalRecordProcedureResults.java (deleted)
-
models/domain/MedicalReport.java (modified) (1 diff)
-
models/domain/Patient.java (modified) (4 diffs)
-
models/domain/PerformedLabTests.java (modified) (2 diffs)
-
models/domain/PerformedProcedures.java (modified) (1 diff)
-
models/domain/Prescriptions.java (modified) (1 diff)
-
models/domain/Procedure.java (modified) (4 diffs)
-
models/domain/ProcedureResults.java (modified) (1 diff)
-
models/domain/Referrals.java (modified) (5 diffs)
-
models/domain/ReportAllergy.java (deleted)
-
models/domain/ReportDiagnosis.java (deleted)
-
models/domain/ReportPrescription.java (deleted)
-
models/domain/ReportSymptom.java (deleted)
-
models/domain/User.java (modified) (1 diff)
-
models/domain/id/DiagnosisProcedureId.java (deleted)
-
models/domain/id/DoctorMedicalRecordId.java (deleted)
-
models/domain/id/MedicalRecordProcedureResultId.java (deleted)
-
models/domain/id/ReportAllergyId.java (deleted)
-
models/domain/id/ReportDiagnosisId.java (deleted)
-
models/domain/id/ReportPrescriptionId.java (deleted)
-
models/domain/id/ReportSymptomId.java (deleted)
-
models/enums/AppointmentStatus.java (modified) (1 diff)
-
repository/AdminRepository.java (modified) (1 diff)
-
repository/AppointmentRepository.java (modified) (1 diff)
-
repository/BillingLabTestsRepository.java (modified) (2 diffs)
-
repository/BillingProceduresRepository.java (modified) (2 diffs)
-
repository/BillingRepository.java (modified) (1 diff)
-
repository/DiagnosisRepository.java (modified) (1 diff)
-
repository/DoctorRepository.java (modified) (1 diff)
-
repository/LabResultsRepository.java (modified) (1 diff)
-
repository/LabTechnicianRepository.java (modified) (1 diff)
-
repository/LabTestRepository.java (modified) (1 diff)
-
repository/MedicalRecordProcedureResultRepository.java (deleted)
-
repository/MedicalRecordRepository.java (modified) (1 diff)
-
repository/MedicalReportRepository.java (modified) (1 diff)
-
repository/PatientRepository.java (modified) (1 diff)
-
repository/PerformedLabTestRepository.java (modified) (1 diff)
-
repository/PerformedProcedureRepository.java (modified) (1 diff)
-
repository/PrescriptionRepository.java (modified) (1 diff)
-
repository/ProcedureRepository.java (modified) (1 diff)
-
repository/ProcedureResultRepository.java (modified) (2 diffs)
-
repository/ReferralRepository.java (modified) (1 diff)
-
repository/ReportAllergyRepository.java (deleted)
-
repository/ReportDiagnosisRepository.java (deleted)
-
repository/ReportPrescriptionRepository.java (deleted)
-
repository/ReportSymptomRepository.java (deleted)
-
service/AppointmentService.java (modified) (1 diff)
-
service/AuthService.java (modified) (3 diffs)
-
service/BillingService.java (modified) (5 diffs)
-
service/DiagnosisService.java (modified) (1 diff)
-
service/DoctorService.java (modified) (6 diffs)
-
service/LabService.java (modified) (6 diffs)
-
service/MedicalReportService.java (modified) (13 diffs)
-
service/PatientService.java (modified) (6 diffs)
-
service/PrescriptionService.java (modified) (1 diff)
-
service/ProcedureService.java (modified) (6 diffs)
-
service/ReferralService.java (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
backend/src/main/java/medora/controller/DoctorController.java
rae6cd79 r48d2bed 63 63 .body(Map.of("error", "Email is required")); 64 64 } 65 if (request.getUsername() == null || request.getUsername().isBlank()) { 66 return ResponseEntity.badRequest() 67 .body(Map.of("error", "Username is required")); 68 } 69 if (request.getPassword() == null || request.getPassword().isBlank()) { 70 return ResponseEntity.badRequest() 71 .body(Map.of("error", "Password is required")); 72 } 65 73 66 74 Doctors doctor = new Doctors(); … … 81 89 doctor.setDepartment(department); 82 90 83 Doctors createdDoctor = doctorService.createDoctor(doctor );91 Doctors createdDoctor = doctorService.createDoctor(doctor, request.getUsername(), request.getPassword()); 84 92 DoctorDTO dto = convertToDTO(createdDoctor); 85 93 -
backend/src/main/java/medora/controller/LabController.java
rae6cd79 r48d2bed 125 125 @PutMapping("/{testId}") 126 126 public ResponseEntity<?> updateLabTest(@PathVariable Long testId, 127 @RequestBody CreateLabTestRequest request) {127 @RequestBody CreateLabTestRequest request) { 128 128 try { 129 129 logger.info("Updating lab test with ID: {}", testId); … … 164 164 logger.info("Requesting lab test {} for patient {}", request.getTestId(), request.getPatientId()); 165 165 166 if (request.getTechnicianId() == null || request.getTechnicianId() <= 0) { 167 return ResponseEntity.badRequest() 168 .body(Map.of("error", "Valid lab technician ID is required")); 169 } 170 166 171 PerformedLabTests performedTest = labService.requestLabTestForPatient( 167 172 request.getPatientId(), 168 173 request.getDoctorId(), 169 174 request.getTestId(), 175 request.getTechnicianId(), 170 176 request.getTestDate(), 171 177 request.getNotes() -
backend/src/main/java/medora/controller/PatientController.java
rae6cd79 r48d2bed 57 57 .body(Map.of("error", "EMBG is required")); 58 58 } 59 if (request.getPassword() == null || request.getPassword().isBlank()) { 60 return ResponseEntity.badRequest() 61 .body(Map.of("error", "Password is required")); 62 } 59 63 60 64 Patient patient = new Patient(); … … 68 72 patient.setEmbg(request.getEmbg()); 69 73 70 Patient createdPatient = patientService.createPatient(patient );74 Patient createdPatient = patientService.createPatient(patient, request.getPassword()); 71 75 PatientDTO dto = convertToDTO(createdPatient); 72 76 -
backend/src/main/java/medora/controller/ReferralController.java
rae6cd79 r48d2bed 13 13 import jakarta.servlet.http.HttpServletRequest; 14 14 15 import java.time.LocalDate; 16 import java.time.LocalTime; 15 17 import java.util.List; 16 18 import java.util.Map; … … 81 83 ); 82 84 83 ReferralDTO dto = convertToDTO(referral );85 ReferralDTO dto = convertToDTO(referral, request.getAppointmentDate(), request.getAppointmentTime()); 84 86 return ResponseEntity.status(HttpStatus.CREATED).body(dto); 85 87 } catch (RuntimeException e) { … … 213 215 214 216 private ReferralDTO convertToDTO(Referrals referral) { 217 return convertToDTO(referral, null, null); 218 } 219 220 // Referrals no longer stores its own appointment_date/appointment_time 221 // (that data lives on the automatically-created Appointment row instead, 222 // matching the validated design). The create endpoint passes the values 223 // it just used to create that appointment; other endpoints have no 224 // reliable way to reconstruct them and pass null. 225 private ReferralDTO convertToDTO(Referrals referral, LocalDate appointmentDate, LocalTime appointmentTime) { 215 226 String fromDoctorName = ""; 216 227 Long fromDoctorId = null; … … 250 261 referral.getReason(), 251 262 referral.getReferralDate(), 252 referral.getAppointmentDate(),253 referral.getAppointmentTime()263 appointmentDate, 264 appointmentTime 254 265 ); 255 266 } -
backend/src/main/java/medora/dto/CreateDoctorRequest.java
rae6cd79 r48d2bed 18 18 private Long specializationId; 19 19 private Long departmentId; 20 private String username; 21 private String password; 20 22 } -
backend/src/main/java/medora/dto/CreatePatientRequest.java
rae6cd79 r48d2bed 23 23 private String phoneNumber; 24 24 private String embg; 25 private String password; 25 26 } -
backend/src/main/java/medora/dto/RequestLabTestRequest.java
rae6cd79 r48d2bed 16 16 private Long doctorId; 17 17 private Long testId; 18 private Long technicianId; 18 19 private LocalDate testDate; 19 20 private String notes; -
backend/src/main/java/medora/models/domain/Admin.java
rae6cd79 r48d2bed 15 15 private Long adminId; 16 16 17 @Column(name = "username", nullable = false, unique = true) 18 private String username; 19 20 @Column(name = "name", nullable = false) 21 private String name; 22 23 @Column(name = "lastname", nullable = false) 24 private String lastname; 25 26 @Column(name = "email", nullable = false, unique = true) 27 private String email; 28 17 29 @OneToOne(optional = false) 18 30 @JoinColumn(name = "user_id", nullable = false, unique = true) 19 31 private User user; 20 32 21 @Column(name = "permissions")22 private String permissions;23 24 33 public Admin() {} 25 34 26 public Admin(Long adminId, User user, String permissions) {35 public Admin(Long adminId, String username, String name, String lastname, String email, User user) { 27 36 this.adminId = adminId; 28 this.user = user; 29 this.permissions = permissions; 30 } 31 32 public Admin(Long adminId, User user) { 33 this.adminId = adminId; 37 this.username = username; 38 this.name = name; 39 this.lastname = lastname; 40 this.email = email; 34 41 this.user = user; 35 42 } -
backend/src/main/java/medora/models/domain/Appointment.java
rae6cd79 r48d2bed 16 16 17 17 @Id 18 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "appointment_seq")19 @SequenceGenerator(name = "appointment_seq", sequenceName = "appointment_id_seq", allocationSize = 1)20 18 @Column(name = "appointment_id") 21 19 private Long appointmentId; -
backend/src/main/java/medora/models/domain/Billing.java
rae6cd79 r48d2bed 19 19 20 20 @Id 21 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bill_seq")22 @SequenceGenerator(name = "bill_seq", sequenceName = "bill_id_seq", allocationSize = 1)23 21 @Column(name = "bill_id") 24 22 private Long billId; … … 39 37 private MedicalRecord medicalRecord; 40 38 41 @ManyToOne(optional = true)42 @JoinColumn(name = "admin_id", nullable = true)39 @ManyToOne(optional = false) 40 @JoinColumn(name = "admin_id", nullable = false) 43 41 private Admin admin; 44 42 -
backend/src/main/java/medora/models/domain/Diagnosis.java
rae6cd79 r48d2bed 12 12 13 13 @Id 14 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "diagnosis_seq")15 @SequenceGenerator(name = "diagnosis_seq", sequenceName = "diagnosis_id_seq", allocationSize = 1)16 14 @Column(name = "diagnosis_id") 17 15 private Long diagnosisId; -
backend/src/main/java/medora/models/domain/Doctors.java
rae6cd79 r48d2bed 12 12 13 13 @Id 14 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "doctor_seq")15 @SequenceGenerator(name = "doctor_seq", sequenceName = "doctor_id_seq", allocationSize = 1)16 14 @Column(name = "doctor_id") 17 15 private Long doctorId; … … 38 36 private Departments department; 39 37 38 @OneToOne(fetch = FetchType.LAZY, optional = false) 39 @JoinColumn(name = "user_id", nullable = false, unique = true) 40 private User user; 41 40 42 public Doctors() {} 41 43 42 44 public Doctors(Long doctorId, String firstName, String lastName, String emailAddress, 43 DoctorLevel level, DoctorSpecialization specialization, Departments department) { 45 DoctorLevel level, DoctorSpecialization specialization, Departments department, 46 User user) { 44 47 this.doctorId = doctorId; 45 48 this.firstName = firstName; … … 49 52 this.specialization = specialization; 50 53 this.department = department; 54 this.user = user; 51 55 } 52 56 } -
backend/src/main/java/medora/models/domain/LabResults.java
rae6cd79 r48d2bed 15 15 16 16 @Id 17 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "result_seq")18 @SequenceGenerator(name = "result_seq", sequenceName = "result_id_seq", allocationSize = 1)19 17 @Column(name = "result_id") 20 18 private Long resultId; -
backend/src/main/java/medora/models/domain/LabTechnician.java
rae6cd79 r48d2bed 15 15 private Long technicianId; 16 16 17 @Column(name = "username", nullable = false, unique = true) 18 private String username; 19 20 @Column(name = "name", nullable = false) 21 private String name; 22 23 @Column(name = "lastname", nullable = false) 24 private String lastname; 25 26 @Column(name = "email", nullable = false, unique = true) 27 private String email; 28 17 29 @OneToOne(optional = false) 18 30 @JoinColumn(name = "user_id", nullable = false, unique = true) 19 31 private User user; 20 32 21 @Column(name = "certification")22 private String certification;23 24 33 public LabTechnician() {} 25 34 26 public LabTechnician(Long technicianId, User user, String certification) {35 public LabTechnician(Long technicianId, String username, String name, String lastname, String email, User user) { 27 36 this.technicianId = technicianId; 28 this.user = user; 29 this.certification = certification; 30 } 31 32 public LabTechnician(Long technicianId, User user) { 33 this.technicianId = technicianId; 37 this.username = username; 38 this.name = name; 39 this.lastname = lastname; 40 this.email = email; 34 41 this.user = user; 35 42 } -
backend/src/main/java/medora/models/domain/LabTests.java
rae6cd79 r48d2bed 17 17 18 18 @Id 19 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_seq")20 @SequenceGenerator(name = "test_seq", sequenceName = "test_id_seq", allocationSize = 1)21 19 @Column(name = "test_id") 22 20 private Long testId; -
backend/src/main/java/medora/models/domain/MedicalRecord.java
rae6cd79 r48d2bed 13 13 14 14 @Id 15 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "record_seq")16 @SequenceGenerator(name = "record_seq", sequenceName = "record_id_seq", allocationSize = 1)17 15 @Column(name = "record_id") 18 16 private Long recordId; -
backend/src/main/java/medora/models/domain/MedicalReport.java
rae6cd79 r48d2bed 17 17 18 18 @Id 19 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "medical_report_seq")20 @SequenceGenerator(name = "medical_report_seq", sequenceName = "medical_report_id_seq", initialValue = 100000, allocationSize = 1)21 19 @Column(name = "report_id") 22 20 private Long reportId; -
backend/src/main/java/medora/models/domain/Patient.java
rae6cd79 r48d2bed 20 20 21 21 @Id 22 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "patient_seq")23 @SequenceGenerator(name = "patient_seq", sequenceName = "patient_id_seq", allocationSize = 1)24 22 @Column(name = "patient_id") 25 23 private Long patientId; … … 55 53 private List<MedicalRecord> medicalRecords; 56 54 55 @OneToOne(fetch = FetchType.LAZY, optional = false) 56 @JoinColumn(name = "user_id", nullable = false, unique = true) 57 private User user; 58 57 59 public Patient() {} 58 60 … … 65 67 Gender gender, 66 68 String phoneNumber, 67 String embg) { 69 String embg, 70 User user) { 68 71 69 72 this.patientId = patientId; … … 76 79 this.phoneNumber = phoneNumber; 77 80 this.embg = embg; 81 this.user = user; 78 82 } 79 83 } -
backend/src/main/java/medora/models/domain/PerformedLabTests.java
rae6cd79 r48d2bed 15 15 16 16 @Id 17 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "performed_test_seq")18 @SequenceGenerator(name = "performed_test_seq", sequenceName = "performed_test_id_seq", allocationSize = 1)19 17 @Column(name = "performed_test_id") 20 18 private Long performedTestId; … … 32 30 private Doctors doctor; 33 31 34 @ManyToOne( fetch = FetchType.LAZY)35 @JoinColumn(name = "technician_id" )32 @ManyToOne(optional = false, fetch = FetchType.LAZY) 33 @JoinColumn(name = "technician_id", nullable = false) 36 34 private LabTechnician technician; 37 35 -
backend/src/main/java/medora/models/domain/PerformedProcedures.java
rae6cd79 r48d2bed 14 14 15 15 @Id 16 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "performed_id_seq")17 @SequenceGenerator(name = "performed_id_seq", sequenceName = "performed_procedures_performed_id_seq", allocationSize = 1)18 16 @Column(name = "performed_id") 19 17 private Long performedId; -
backend/src/main/java/medora/models/domain/Prescriptions.java
rae6cd79 r48d2bed 13 13 14 14 @Id 15 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "prescription_seq")16 @SequenceGenerator(name = "prescription_seq", sequenceName = "prescription_id_seq", allocationSize = 1)17 15 @Column(name = "prescription_id") 18 16 private Long prescriptionId; -
backend/src/main/java/medora/models/domain/Procedure.java
rae6cd79 r48d2bed 8 8 9 9 import java.math.BigDecimal; 10 import java.time.LocalDate; 10 11 11 12 @Getter … … 16 17 17 18 @Id 18 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "procedure_seq")19 @SequenceGenerator(name = "procedure_seq", sequenceName = "procedure_id_seq", allocationSize = 1)20 19 @Column(name = "procedure_id") 21 20 private Long procedureId; … … 24 23 @Column(name = "procedure_type", nullable = false) 25 24 private String procedureType; 25 26 @Column(name = "procedure_date", nullable = false) 27 private LocalDate procedureDate; 26 28 27 29 @Column(name = "description", columnDefinition = "TEXT") … … 32 34 private BigDecimal cost; 33 35 36 @ManyToOne(optional = false) 37 @JoinColumn(name = "doctor_id", nullable = false) 38 private Doctors doctor; 39 34 40 public Procedure() {} 35 41 36 42 public Procedure(Long procedureId, 37 43 String procedureType, 44 LocalDate procedureDate, 38 45 String description, 39 BigDecimal cost) { 46 BigDecimal cost, 47 Doctors doctor) { 40 48 41 49 this.procedureId = procedureId; 42 50 this.procedureType = procedureType; 51 this.procedureDate = procedureDate; 43 52 this.description = description; 44 53 this.cost = cost; 54 this.doctor = doctor; 45 55 } 46 56 } -
backend/src/main/java/medora/models/domain/ProcedureResults.java
rae6cd79 r48d2bed 16 16 17 17 @Id 18 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "result_seq")19 @SequenceGenerator(name = "result_seq", sequenceName = "procedure_results_result_id_seq", allocationSize = 1)20 18 @Column(name = "result_id") 21 19 private Long resultId; -
backend/src/main/java/medora/models/domain/Referrals.java
rae6cd79 r48d2bed 8 8 9 9 import java.time.LocalDate; 10 import java.time.LocalTime;11 10 12 11 @Getter … … 17 16 18 17 @Id 19 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "referral_seq")20 @SequenceGenerator(name = "referral_seq", sequenceName = "referral_id_seq", allocationSize = 1)21 18 @Column(name = "referral_id") 22 19 private Long referralId; … … 28 25 @Column(name = "referral_date", nullable = false) 29 26 private LocalDate referralDate; 30 31 @Column(name = "appointment_date")32 private LocalDate appointmentDate;33 34 @Column(name = "appointment_time")35 private LocalTime appointmentTime;36 27 37 28 @ManyToOne(optional = false) … … 52 43 String reason, 53 44 LocalDate referralDate, 54 LocalDate appointmentDate,55 LocalTime appointmentTime,56 45 MedicalRecord medicalRecord, 57 46 Doctors fromDoctor, … … 61 50 this.reason = reason; 62 51 this.referralDate = referralDate; 63 this.appointmentDate = appointmentDate;64 this.appointmentTime = appointmentTime;65 52 this.medicalRecord = medicalRecord; 66 53 this.fromDoctor = fromDoctor; -
backend/src/main/java/medora/models/domain/User.java
rae6cd79 r48d2bed 34 34 private Boolean isActive = true; 35 35 36 // Foreign key to patient (only for PATIENT role)37 @OneToOne(fetch = FetchType.LAZY)38 @JoinColumn(name = "patient_id")39 private Patient patient;40 41 // Foreign key to doctor (only for DOCTOR role)42 @OneToOne(fetch = FetchType.LAZY)43 @JoinColumn(name = "doctor_id")44 private Doctors doctor;45 46 36 public User() {} 47 37 -
backend/src/main/java/medora/models/enums/AppointmentStatus.java
rae6cd79 r48d2bed 5 5 COMPLETED, 6 6 CANCELLED, 7 IN_PROGRESS 7 IN_PROGRESS, 8 NO_SHOW 8 9 } -
backend/src/main/java/medora/repository/AdminRepository.java
rae6cd79 r48d2bed 3 3 import medora.models.domain.Admin; 4 4 import org.springframework.data.jpa.repository.JpaRepository; 5 import org.springframework.data.jpa.repository.Query; 5 6 6 7 public interface AdminRepository extends JpaRepository<Admin, Long> { 8 9 // adminId is NOT auto-generated by the database (supplied by the application), 10 // so callers must compute the next free ID before saving a new Admin. 11 @Query("SELECT COALESCE(MAX(e.adminId), 0) FROM Admin e") 12 Long findMaxAdminId(); 7 13 } 8 14 -
backend/src/main/java/medora/repository/AppointmentRepository.java
rae6cd79 r48d2bed 14 14 15 15 public interface AppointmentRepository extends JpaRepository<Appointment, Long> { 16 17 // appointmentId is NOT auto-generated by the database (supplied by the application), 18 // so callers must compute the next free ID before saving a new Appointment. 19 @Query("SELECT COALESCE(MAX(e.appointmentId), 0) FROM Appointment e") 20 Long findMaxAppointmentId(); 16 21 17 22 /** -
backend/src/main/java/medora/repository/BillingLabTestsRepository.java
rae6cd79 r48d2bed 19 19 List<BillingLabTests> findByBillingBillId(@Param("billId") Long billId); 20 20 21 boolean existsByBillingBillIdAndLabTestTestId(Long billId, Long testId); 22 21 23 // Calculate total cost of lab tests for a billing record 22 24 @Query(""" … … 26 28 BigDecimal calculateTotalCostForBilling(@Param("billId") Long billId); 27 29 } 28 -
backend/src/main/java/medora/repository/BillingProceduresRepository.java
rae6cd79 r48d2bed 19 19 List<BillingProcedures> findByBillingBillId(@Param("billId") Long billId); 20 20 21 boolean existsByBillingBillIdAndProcedureProcedureId(Long billId, Long procedureId); 22 21 23 // Calculate total cost of procedures for a billing record 22 24 @Query(""" … … 26 28 BigDecimal calculateTotalCostForBilling(@Param("billId") Long billId); 27 29 } 28 -
backend/src/main/java/medora/repository/BillingRepository.java
rae6cd79 r48d2bed 13 13 14 14 public interface BillingRepository extends JpaRepository<Billing, Long> { 15 16 // billId is NOT auto-generated by the database (supplied by the application), 17 // so callers must compute the next free ID before saving a new Billing. 18 @Query("SELECT COALESCE(MAX(e.billId), 0) FROM Billing e") 19 Long findMaxBillId(); 15 20 16 21 // UC020 – Generate Billing Record -
backend/src/main/java/medora/repository/DiagnosisRepository.java
rae6cd79 r48d2bed 10 10 11 11 public interface DiagnosisRepository extends JpaRepository<Diagnosis, Long> { 12 13 // diagnosis_id is NOT auto-generated by the database (supplied by the application), 14 // so callers must compute the next free ID before saving a new Diagnosis. 15 @Query("SELECT COALESCE(MAX(d.diagnosisId), 0) FROM Diagnosis d") 16 Long findMaxDiagnosisId(); 17 12 18 //UC009 – Record Diagnosis 13 19 // A doctor adds a diagnosis to a patient’s medical record. -
backend/src/main/java/medora/repository/DoctorRepository.java
rae6cd79 r48d2bed 11 11 12 12 public interface DoctorRepository extends JpaRepository<Doctors, Long> { 13 14 // doctorId is NOT auto-generated by the database (supplied by the application), 15 // so callers must compute the next free ID before saving a new Doctors. 16 @Query("SELECT COALESCE(MAX(e.doctorId), 0) FROM Doctors e") 17 Long findMaxDoctorId(); 18 19 // Login lookup: find the doctor profile linked to a given user account 20 // (Doctors.user_id is the foreign key, the reverse of Users->Doctors) 21 Optional<Doctors> findByUserUserId(Long userId); 13 22 14 23 // UC023 – View Departments (see DepartmentRepository) -
backend/src/main/java/medora/repository/LabResultsRepository.java
rae6cd79 r48d2bed 9 9 10 10 public interface LabResultsRepository extends JpaRepository<LabResults, Long> { 11 12 // resultId is NOT auto-generated by the database (supplied by the application), 13 // so callers must compute the next free ID before saving a new LabResults. 14 @Query("SELECT COALESCE(MAX(e.resultId), 0) FROM LabResults e") 15 Long findMaxResultId(); 11 16 12 17 // UC014 – Store Lab Results -
backend/src/main/java/medora/repository/LabTechnicianRepository.java
rae6cd79 r48d2bed 3 3 import medora.models.domain.LabTechnician; 4 4 import org.springframework.data.jpa.repository.JpaRepository; 5 import org.springframework.data.jpa.repository.Query; 5 6 6 7 public interface LabTechnicianRepository extends JpaRepository<LabTechnician, Long> { 8 9 // technicianId is NOT auto-generated by the database (supplied by the application), 10 // so callers must compute the next free ID before saving a new LabTechnician. 11 @Query("SELECT COALESCE(MAX(e.technicianId), 0) FROM LabTechnician e") 12 Long findMaxTechnicianId(); 7 13 } -
backend/src/main/java/medora/repository/LabTestRepository.java
rae6cd79 r48d2bed 10 10 11 11 public interface LabTestRepository extends JpaRepository<LabTests, Long> { 12 13 // testId is NOT auto-generated by the database (supplied by the application), 14 // so callers must compute the next free ID before saving a new LabTests. 15 @Query("SELECT COALESCE(MAX(e.testId), 0) FROM LabTests e") 16 Long findMaxTestId(); 12 17 13 18 // UC013 – Record Lab Test Request -
backend/src/main/java/medora/repository/MedicalRecordRepository.java
rae6cd79 r48d2bed 11 11 12 12 public interface MedicalRecordRepository extends JpaRepository<MedicalRecord, Long> { 13 14 // recordId is NOT auto-generated by the database (supplied by the application), 15 // so callers must compute the next free ID before saving a new MedicalRecord. 16 @Query("SELECT COALESCE(MAX(e.recordId), 0) FROM MedicalRecord e") 17 Long findMaxRecordId(); 13 18 14 19 // UC005 – View Medical Record (Full history) -
backend/src/main/java/medora/repository/MedicalReportRepository.java
rae6cd79 r48d2bed 15 15 // This properly manages entity lifecycle and relationships 16 16 17 // report_id is NOT auto-generated by the database (supplied by the application), 18 // so callers must compute the next free ID before saving a new MedicalReport. 19 @Query("SELECT COALESCE(MAX(mr.reportId), 0) FROM MedicalReport mr") 20 Long findMaxReportId(); 21 17 22 // UC005 – Retrieve medical reports for a medical record 18 23 List<MedicalReport> findByMedicalRecordRecordId(Long recordId); -
backend/src/main/java/medora/repository/PatientRepository.java
rae6cd79 r48d2bed 10 10 11 11 public interface PatientRepository extends JpaRepository<Patient, Long> { 12 13 // patientId is NOT auto-generated by the database (supplied by the application), 14 // so callers must compute the next free ID before saving a new Patient. 15 @Query("SELECT COALESCE(MAX(e.patientId), 0) FROM Patient e") 16 Long findMaxPatientId(); 17 18 // Login lookup: find the patient profile linked to a given user account 19 // (Patient.user_id is the foreign key, the reverse of Users->Patient) 20 Optional<Patient> findByUserUserId(Long userId); 12 21 13 22 // UC004 – View Patient Profile -
backend/src/main/java/medora/repository/PerformedLabTestRepository.java
rae6cd79 r48d2bed 10 10 11 11 public interface PerformedLabTestRepository extends JpaRepository<PerformedLabTests, Long> { 12 13 // performedTestId is NOT auto-generated by the database (supplied by the application), 14 // so callers must compute the next free ID before saving a new PerformedLabTests. 15 @Query("SELECT COALESCE(MAX(e.performedTestId), 0) FROM PerformedLabTests e") 16 Long findMaxPerformedTestId(); 12 17 13 18 List<PerformedLabTests> findByPatientPatientId(Long patientId); -
backend/src/main/java/medora/repository/PerformedProcedureRepository.java
rae6cd79 r48d2bed 13 13 14 14 public interface PerformedProcedureRepository extends JpaRepository<PerformedProcedures, Long> { 15 16 // performedId is NOT auto-generated by the database (supplied by the application), 17 // so callers must compute the next free ID before saving a new PerformedProcedures. 18 @Query("SELECT COALESCE(MAX(e.performedId), 0) FROM PerformedProcedures e") 19 Long findMaxPerformedId(); 15 20 16 21 // UC016 – Record Procedure Entry -
backend/src/main/java/medora/repository/PrescriptionRepository.java
rae6cd79 r48d2bed 11 11 12 12 public interface PrescriptionRepository extends JpaRepository<Prescriptions, Long> { 13 14 // prescriptionId is NOT auto-generated by the database (supplied by the application), 15 // so callers must compute the next free ID before saving a new Prescriptions. 16 @Query("SELECT COALESCE(MAX(e.prescriptionId), 0) FROM Prescriptions e") 17 Long findMaxPrescriptionId(); 13 18 14 19 // UC012 – Record Prescription -
backend/src/main/java/medora/repository/ProcedureRepository.java
rae6cd79 r48d2bed 3 3 import medora.models.domain.Procedure; 4 4 import org.springframework.data.jpa.repository.JpaRepository; 5 import org.springframework.data.jpa.repository.Query; 5 6 6 7 public interface ProcedureRepository extends JpaRepository<Procedure, Long> { 8 9 // procedureId is NOT auto-generated by the database (supplied by the application), 10 // so callers must compute the next free ID before saving a new Procedure. 11 @Query("SELECT COALESCE(MAX(e.procedureId), 0) FROM Procedure e") 12 Long findMaxProcedureId(); 7 13 } 8 14 -
backend/src/main/java/medora/repository/ProcedureResultRepository.java
rae6cd79 r48d2bed 10 10 public interface ProcedureResultRepository extends JpaRepository<ProcedureResults, Long> { 11 11 12 // result_id is NOT auto-generated by the database (supplied by the application), 13 // so callers must compute the next free ID before saving a new ProcedureResults. 14 @Query("SELECT COALESCE(MAX(pr.resultId), 0) FROM ProcedureResults pr") 15 Long findMaxResultId(); 16 12 17 // UC017 – Record Procedure Outcome 13 18 List<ProcedureResults> findByProcedureProcedureId(Long procedureId); 14 19 15 // Find results for a specific medical record 20 // Find results for a specific medical record. 21 // The schema links a medical record to a procedure (medical_record_procedures), 22 // and a procedure to its results (procedure_results) — there is no direct 23 // record-to-result table, so this joins through the procedure in between, 24 // matching the pattern already validated for UseCase09. 16 25 @Query(""" 17 26 SELECT pr FROM ProcedureResults pr 18 WHERE pr IN (19 SELECT mrp r.procedureResult FROM MedicalRecordProcedureResults mrpr20 WHERE mrp r.medicalRecord.recordId = :recordId27 WHERE pr.procedure.procedureId IN ( 28 SELECT mrp.procedure.procedureId FROM MedicalRecordProcedures mrp 29 WHERE mrp.medicalRecord.recordId = :recordId 21 30 ) 22 31 """) … … 33 42 @Query(""" 34 43 SELECT pr FROM ProcedureResults pr 35 WHERE pr IN ( 36 SELECT mrpr.procedureResult FROM MedicalRecordProcedureResults mrpr 37 WHERE mrpr.medicalRecord.recordId = :recordId 44 WHERE pr.procedure.procedureId = :procedureId 45 AND pr.procedure.procedureId IN ( 46 SELECT mrp.procedure.procedureId FROM MedicalRecordProcedures mrp 47 WHERE mrp.medicalRecord.recordId = :recordId 38 48 ) 39 AND pr.procedure.procedureId = :procedureId40 49 """) 41 50 List<ProcedureResults> findByMedicalRecordAndProcedure(@Param("recordId") Long recordId, @Param("procedureId") Long procedureId); -
backend/src/main/java/medora/repository/ReferralRepository.java
rae6cd79 r48d2bed 9 9 10 10 public interface ReferralRepository extends JpaRepository<Referrals, Long> { 11 12 // referralId is NOT auto-generated by the database (supplied by the application), 13 // so callers must compute the next free ID before saving a new Referrals. 14 @Query("SELECT COALESCE(MAX(e.referralId), 0) FROM Referrals e") 15 Long findMaxReferralId(); 11 16 12 17 // UC019 – Create Referral Record -
backend/src/main/java/medora/service/AppointmentService.java
rae6cd79 r48d2bed 121 121 Appointment appointment = new Appointment(); 122 122 123 appointment.setAppointmentId(appointmentRepository.findMaxAppointmentId() + 1); 123 124 appointment.setPatient(patient); 124 125 appointment.setDoctor(doctor); -
backend/src/main/java/medora/service/AuthService.java
rae6cd79 r48d2bed 3 3 import medora.models.domain.User; 4 4 import medora.repository.UserRepository; 5 import medora.repository.DoctorRepository; 6 import medora.repository.PatientRepository; 5 7 import medora.util.JwtUtil; 6 8 import org.slf4j.Logger; … … 21 23 22 24 private final UserRepository userRepository; 25 private final DoctorRepository doctorRepository; 26 private final PatientRepository patientRepository; 23 27 private final JwtUtil jwtUtil; 24 28 private final PasswordEncoder passwordEncoder; 25 29 26 public AuthService(UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder) { 30 public AuthService(UserRepository userRepository, 31 DoctorRepository doctorRepository, 32 PatientRepository patientRepository, 33 JwtUtil jwtUtil, 34 PasswordEncoder passwordEncoder) { 27 35 this.userRepository = userRepository; 36 this.doctorRepository = doctorRepository; 37 this.patientRepository = patientRepository; 28 38 this.jwtUtil = jwtUtil; 29 39 this.passwordEncoder = passwordEncoder; … … 58 68 } 59 69 60 Long patientId = user.getPatient() != null ? user.getPatient().getPatientId() : null; 61 Long doctorId = user.getDoctor() != null ? user.getDoctor().getDoctorId() : null; 70 // Patient/Doctor no longer carry a reverse pointer on User — the FK 71 // lives the other way round (Patient.user_id / Doctors.user_id), so 72 // the linked profile (if any) is looked up by user_id instead. 73 Long patientId = patientRepository.findByUserUserId(user.getUserId()) 74 .map(p -> p.getPatientId()) 75 .orElse(null); 76 Long doctorId = doctorRepository.findByUserUserId(user.getUserId()) 77 .map(d -> d.getDoctorId()) 78 .orElse(null); 62 79 String token = jwtUtil.generateTokenWithDoctorId(user.getUsername(), user.getRole(), user.getUserId(), patientId, doctorId); 63 80 -
backend/src/main/java/medora/service/BillingService.java
rae6cd79 r48d2bed 91 91 92 92 Billing billing = new Billing(); 93 billing.setBillId(billingRepository.findMaxBillId() + 1); 93 94 billing.setMedicalRecord(medicalRecord); 95 billing.setTotalCost(totalCost); 94 96 billing.setAdmin(admin); 95 billing.setTotalCost(totalCost);96 97 billing.setPaymentStatus(PaymentStatus.PENDING); 97 98 logger.info("Generating billing record for medical record ID: {} with total cost: {}",99 medicalRecordId, totalCost);100 98 return billingRepository.save(billing); 101 99 } … … 292 290 logger.info("Creating new medical record for patient {}", patientId); 293 291 MedicalRecord newRecord = new MedicalRecord(); 292 newRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1); 294 293 newRecord.setPatient(patientRepository.findById(patientId) 295 294 .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + patientId))); … … 354 353 // Create new billing record 355 354 billing = new Billing(); 355 billing.setBillId(billingRepository.findMaxBillId() + 1); 356 356 billing.setMedicalRecord(medicalRecord); 357 357 billing.setAdmin(admin); … … 369 369 // Link procedures to billing (only if not already linked) 370 370 for (PerformedProcedures procedure : procedures) { 371 try { 371 if (!billingProceduresRepository.existsByBillingBillIdAndProcedureProcedureId( 372 savedBilling.getBillId(), procedure.getProcedure().getProcedureId())) { 372 373 BillingProcedures billingProcedure = new BillingProcedures(savedBilling, procedure.getProcedure()); 373 374 billingProceduresRepository.save(billingProcedure); 374 375 logger.debug("Linked procedure {} to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId()); 375 } catch (Exception e) {376 logger.debug("Procedure {} already linked to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId());377 376 } 378 377 } … … 380 379 // Link lab tests to billing (only if not already linked) 381 380 for (PerformedLabTests labTest : labTests) { 382 try { 381 if (!billingLabTestsRepository.existsByBillingBillIdAndLabTestTestId( 382 savedBilling.getBillId(), labTest.getLabTest().getTestId())) { 383 383 BillingLabTests billingLabTest = new BillingLabTests(savedBilling, labTest.getLabTest()); 384 384 billingLabTestsRepository.save(billingLabTest); 385 385 logger.debug("Linked lab test {} to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId()); 386 } catch (Exception e) {387 logger.debug("Lab test {} already linked to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId());388 386 } 389 387 } -
backend/src/main/java/medora/service/DiagnosisService.java
rae6cd79 r48d2bed 60 60 61 61 Diagnosis diagnosis = new Diagnosis(); 62 diagnosis.setDiagnosisId(diagnosisRepository.findMaxDiagnosisId() + 1); 62 63 diagnosis.setName(diagnosisName); 63 64 diagnosis.setDescription(description); -
backend/src/main/java/medora/service/DoctorService.java
rae6cd79 r48d2bed 3 3 import medora.models.domain.Doctors; 4 4 import medora.models.domain.Departments; 5 import medora.models.domain.User; 5 6 import medora.repository.DoctorRepository; 6 7 import medora.repository.DepartmentRepository; 8 import medora.repository.UserRepository; 7 9 import org.slf4j.Logger; 8 10 import org.slf4j.LoggerFactory; 11 import org.springframework.security.crypto.password.PasswordEncoder; 9 12 import org.springframework.stereotype.Service; 10 13 import org.springframework.transaction.annotation.Transactional; … … 24 27 private final DoctorRepository doctorRepository; 25 28 private final DepartmentRepository departmentRepository; 29 private final UserRepository userRepository; 30 private final PasswordEncoder passwordEncoder; 26 31 27 32 public DoctorService(DoctorRepository doctorRepository, 28 DepartmentRepository departmentRepository) { 33 DepartmentRepository departmentRepository, 34 UserRepository userRepository, 35 PasswordEncoder passwordEncoder) { 29 36 this.doctorRepository = doctorRepository; 30 37 this.departmentRepository = departmentRepository; 38 this.userRepository = userRepository; 39 this.passwordEncoder = passwordEncoder; 31 40 } 32 41 … … 119 128 */ 120 129 @Transactional 121 public Doctors createDoctor(Doctors doctor ) {130 public Doctors createDoctor(Doctors doctor, String username, String rawPassword) { 122 131 123 132 if (doctor == null) { … … 139 148 if (doctor.getDepartment() == null || doctor.getDepartment().getDepartmentId() == null) { 140 149 throw new IllegalArgumentException("Doctor department is required"); 150 } 151 152 if (username == null || username.isBlank()) { 153 throw new IllegalArgumentException("Username is required"); 154 } 155 156 if (rawPassword == null || rawPassword.isBlank()) { 157 throw new IllegalArgumentException("Password is required"); 141 158 } 142 159 … … 144 161 if (doctorRepository.findByEmailAddress(doctor.getEmailAddress()).isPresent()) { 145 162 throw new RuntimeException("Doctor with this email already exists"); 163 } 164 165 if (userRepository.existsByUsername(username)) { 166 throw new RuntimeException("Username already taken"); 146 167 } 147 168 … … 153 174 doctor.setDepartment(department); 154 175 176 // Create the login account first (user_id is DB-generated), since 177 // Doctors.user_id is a required foreign key pointing to it. 178 User user = new User(); 179 user.setUsername(username); 180 user.setPassword(passwordEncoder.encode(rawPassword)); 181 user.setRole("DOCTOR"); 182 user.setFirstName(doctor.getFirstName()); 183 user.setLastName(doctor.getLastName()); 184 user.setIsActive(true); 185 User savedUser = userRepository.save(user); 186 187 doctor.setUser(savedUser); 188 doctor.setDoctorId(doctorRepository.findMaxDoctorId() + 1); 189 155 190 logger.info("Creating new doctor: {} {}", doctor.getFirstName(), doctor.getLastName()); 156 191 -
backend/src/main/java/medora/service/LabService.java
rae6cd79 r48d2bed 66 66 67 67 LabTests labTest = new LabTests(); 68 labTest.setTestId(labTestRepository.findMaxTestId() + 1); 68 69 labTest.setTestName(testName); 69 70 labTest.setDescription(description); … … 113 114 Long doctorId, 114 115 Long testId, 116 Long technicianId, 115 117 LocalDate testDate, 116 118 String notes) { … … 124 126 throw new IllegalArgumentException("Invalid test ID"); 125 127 128 if (technicianId == null || technicianId <= 0) 129 throw new IllegalArgumentException("Invalid lab technician ID"); 130 126 131 Patient patient = patientRepository.findById(patientId) 127 132 .orElseThrow(() -> new RuntimeException("Patient not found")); … … 133 138 .orElseThrow(() -> new RuntimeException("Lab test not found")); 134 139 140 LabTechnician technician = labTechnicianRepository.findById(technicianId) 141 .orElseThrow(() -> new RuntimeException("Lab technician not found")); 142 135 143 PerformedLabTests performedTest = new PerformedLabTests(); 144 performedTest.setPerformedTestId(performedLabTestRepository.findMaxPerformedTestId() + 1); 136 145 performedTest.setPatient(patient); 137 146 performedTest.setDoctor(doctor); 138 147 performedTest.setLabTest(test); 148 performedTest.setTechnician(technician); 139 149 LocalDate finalTestDate = testDate != null ? testDate : LocalDate.now(); 140 150 performedTest.setTestDate(finalTestDate); … … 189 199 throw new IllegalArgumentException("Results required"); 190 200 201 LocalDate finalResultDate = resultDate != null ? resultDate : LocalDate.now(); 202 if (finalResultDate.isAfter(LocalDate.now())) 203 throw new IllegalArgumentException("Result date cannot be in the future"); 204 191 205 MedicalRecord medicalRecord = medicalRecordRepository.findById(medicalRecordId) 192 206 .orElseThrow(() -> new RuntimeException("Medical record not found")); … … 196 210 197 211 LabResults labResult = new LabResults(); 212 labResult.setResultId(labResultsRepository.findMaxResultId() + 1); 198 213 labResult.setResults(results); 199 labResult.setResultDate( resultDate);214 labResult.setResultDate(finalResultDate); 200 215 labResult.setLabTest(labTest); 201 216 -
backend/src/main/java/medora/service/MedicalReportService.java
rae6cd79 r48d2bed 10 10 11 11 import java.time.LocalDate; 12 import java.util.HashSet;13 12 import java.util.List; 14 13 import java.util.Optional; 15 import java.util.Set;16 14 import java.util.stream.Collectors; 17 15 … … 20 18 * UC018 – Create Medical Report 21 19 * OPTIONAL - Use only if needed 20 * 21 * NOTE: This service was refactored to match the final database schema, which 22 * has no report_diagnosis / report_prescription / report_allergy / report_symptom 23 * junction tables. A medical report therefore no longer stores its own 24 * per-report subset of diagnoses/prescriptions/allergies/symptoms — instead, 25 * a "comprehensive report" simply reflects everything already linked to the 26 * report's underlying medical record (via Diagnosis.patient, PrescriptionMedicalRecord, 27 * MedicalRecordAllergies, and MedicalRecordSymptoms), the same way the record's 28 * data is shown elsewhere in the application. 22 29 */ 23 30 @Service … … 33 40 private final MedicalRecordAllergyRepository medicalRecordAllergyRepository; 34 41 private final MedicalRecordSymptomRepository symptomRepository; 35 private final ReportDiagnosisRepository reportDiagnosisRepository;36 private final ReportPrescriptionRepository reportPrescriptionRepository;37 private final ReportAllergyRepository reportAllergyRepository;38 private final ReportSymptomRepository reportSymptomRepository;39 private final PrescriptionRepository prescriptionRepository;40 private final AllergyRepository allergyRepository;41 private final SymptomRepository symptomDbRepository;42 42 43 43 public MedicalReportService(MedicalReportRepository medicalReportRepository, … … 47 47 PrescriptionMedicalRecordRepository prescriptionMedicalRecordRepository, 48 48 MedicalRecordAllergyRepository medicalRecordAllergyRepository, 49 MedicalRecordSymptomRepository symptomRepository, 50 ReportDiagnosisRepository reportDiagnosisRepository, 51 ReportPrescriptionRepository reportPrescriptionRepository, 52 ReportAllergyRepository reportAllergyRepository, 53 ReportSymptomRepository reportSymptomRepository, 54 PrescriptionRepository prescriptionRepository, 55 AllergyRepository allergyRepository, 56 SymptomRepository symptomDbRepository) { 49 MedicalRecordSymptomRepository symptomRepository) { 57 50 this.medicalReportRepository = medicalReportRepository; 58 51 this.doctorRepository = doctorRepository; … … 62 55 this.medicalRecordAllergyRepository = medicalRecordAllergyRepository; 63 56 this.symptomRepository = symptomRepository; 64 this.reportDiagnosisRepository = reportDiagnosisRepository;65 this.reportPrescriptionRepository = reportPrescriptionRepository;66 this.reportAllergyRepository = reportAllergyRepository;67 this.reportSymptomRepository = reportSymptomRepository;68 this.prescriptionRepository = prescriptionRepository;69 this.allergyRepository = allergyRepository;70 this.symptomDbRepository = symptomDbRepository;71 57 } 72 58 73 59 /** 74 60 * UC018 – Create Medical Report 75 * Create a medical report describing a patient's visit and condition 61 * Create a medical report describing a patient's visit and condition. 62 * report_id is not database-generated, so the next free ID is computed here. 76 63 */ 77 64 @Transactional … … 98 85 99 86 MedicalReport report = new MedicalReport(); 100 // Let JPA auto-generate the ID using the sequence101 report.setReportId(n ull);87 Long nextReportId = medicalReportRepository.findMaxReportId() + 1; 88 report.setReportId(nextReportId); 102 89 report.setDoctor(doctor); 103 90 report.setMedicalRecord(medicalRecord); … … 111 98 112 99 /** 113 * Create a medical report with selected diagnoses, prescriptions, allergies, and symptoms 100 * Create a medical report, optionally validating that the given diagnosis, 101 * prescription, allergy, and symptom IDs are actually linked to the medical 102 * record. The schema has no table to persist a report-specific subset of 103 * these items, so they are only used here as a validation step — the 104 * resulting comprehensive report will reflect everything on the record, 105 * not just the IDs passed in. 114 106 */ 115 107 @Transactional … … 119 111 List<Long> selectedAllergyIds, List<Long> selectedSymptomIds) { 120 112 121 // Create the base report 122 MedicalReport report = createMedicalReport(doctorId, medicalRecordId, description, reportDate); 123 124 // Store selected diagnoses 113 MedicalRecord medicalRecord = medicalRecordRepository.findById(medicalRecordId) 114 .orElseThrow(() -> new RuntimeException("Medical record not found with ID: " + medicalRecordId)); 115 125 116 if (selectedDiagnosisIds != null) { 126 117 for (Long diagnosisId : selectedDiagnosisIds) { 127 Diagnosis diagnosis =diagnosisRepository.findById(diagnosisId)118 diagnosisRepository.findById(diagnosisId) 128 119 .orElseThrow(() -> new RuntimeException("Diagnosis not found with ID: " + diagnosisId)); 129 ReportDiagnosis reportDiagnosis = new ReportDiagnosis(report, diagnosis);130 reportDiagnosisRepository.save(reportDiagnosis);131 120 } 132 121 } 133 122 134 // Store selected prescriptions135 123 if (selectedPrescriptionIds != null) { 136 124 for (Long prescriptionId : selectedPrescriptionIds) { 137 Prescriptions prescription = prescriptionRepository.findById(prescriptionId) 138 .orElseThrow(() -> new RuntimeException("Prescription not found with ID: " + prescriptionId)); 139 ReportPrescription reportPrescription = new ReportPrescription(report, prescription); 140 reportPrescriptionRepository.save(reportPrescription); 125 PrescriptionMedicalRecord pmr = prescriptionMedicalRecordRepository 126 .findByMedicalRecordAndPrescription(medicalRecordId, prescriptionId); 127 if (pmr == null) { 128 throw new RuntimeException("Prescription " + prescriptionId 129 + " is not linked to medical record " + medicalRecordId); 130 } 141 131 } 142 132 } 143 133 144 // Store selected allergies145 134 if (selectedAllergyIds != null) { 146 135 for (Long allergyId : selectedAllergyIds) { 147 Allergies allergy = allergyRepository.findById(allergyId) 148 .orElseThrow(() -> new RuntimeException("Allergy not found with ID: " + allergyId)); 149 ReportAllergy reportAllergy = new ReportAllergy(report, allergy); 150 reportAllergyRepository.save(reportAllergy); 136 boolean exists = medicalRecordAllergyRepository 137 .existsByMedicalRecordRecordIdAndAllergyAllergyId(medicalRecordId, allergyId); 138 if (!exists) { 139 throw new RuntimeException("Allergy " + allergyId 140 + " is not linked to medical record " + medicalRecordId); 141 } 151 142 } 152 143 } 153 144 154 // Store selected symptoms155 145 if (selectedSymptomIds != null) { 156 146 for (Long symptomId : selectedSymptomIds) { 157 Symptoms symptom = symptomDbRepository.findById(symptomId) 158 .orElseThrow(() -> new RuntimeException("Symptom not found with ID: " + symptomId)); 159 ReportSymptom reportSymptom = new ReportSymptom(report, symptom); 160 reportSymptomRepository.save(reportSymptom); 147 boolean exists = symptomRepository 148 .existsByMedicalRecordRecordIdAndSymptomSymptomId(medicalRecordId, symptomId); 149 if (!exists) { 150 throw new RuntimeException("Symptom " + symptomId 151 + " is not linked to medical record " + medicalRecordId); 152 } 161 153 } 162 154 } 163 155 164 logger.info("Created medical report with selected items for record ID: {}", medicalRecordId); 165 return report; 156 logger.info("Creating medical report for record ID: {} (validated {} diagnoses, {} prescriptions, " 157 + "{} allergies, {} symptoms already on the record)", medicalRecordId, 158 selectedDiagnosisIds == null ? 0 : selectedDiagnosisIds.size(), 159 selectedPrescriptionIds == null ? 0 : selectedPrescriptionIds.size(), 160 selectedAllergyIds == null ? 0 : selectedAllergyIds.size(), 161 selectedSymptomIds == null ? 0 : selectedSymptomIds.size()); 162 163 return createMedicalReport(doctorId, medicalRecordId, description, reportDate); 166 164 } 167 165 … … 279 277 280 278 /** 281 * Build a comprehensive report DTO with all medical data 279 * Build a comprehensive report DTO with all medical data linked to the 280 * report's underlying medical record (diagnoses by patient, prescriptions, 281 * allergies, and symptoms already recorded on that record). 282 282 */ 283 283 private ComprehensiveMedicalReportDTO buildComprehensiveReport(MedicalReport report) { … … 285 285 Patient patient = report.getMedicalRecord().getPatient(); 286 286 287 // Get selected diagnoses from linking table 288 List<ReportDiagnosis> reportDiagnoses = reportDiagnosisRepository.findByReportReportId(report.getReportId()); 289 List<Diagnosis> diagnoses = reportDiagnoses.stream() 290 .map(ReportDiagnosis::getDiagnosis) 291 .collect(Collectors.toList()); 287 // Diagnoses: diagnosis links directly to a patient in this schema, 288 // there is no separate report-specific selection table. 289 List<Diagnosis> diagnoses = diagnosisRepository.findByPatientPatientId(patient.getPatientId()); 292 290 293 291 List<DiagnosisDTO> diagnosisDTOs = diagnoses.stream() … … 303 301 .collect(Collectors.toList()); 304 302 305 // Get selected prescriptions from linking table 306 List<ReportPrescription> reportPrescriptions = reportPrescriptionRepository.findByReportReportId(report.getReportId()); 307 List<PrescriptionMedicalRecord> prescriptions = new java.util.ArrayList<>(); 308 for (ReportPrescription rp : reportPrescriptions) { 309 // Find the corresponding PrescriptionMedicalRecord entry 310 List<PrescriptionMedicalRecord> pmr = prescriptionMedicalRecordRepository.findByMedicalRecordRecordIdAndPrescriptionPrescriptionId( 311 medicalRecordId, rp.getPrescription().getPrescriptionId()); 312 prescriptions.addAll(pmr); 313 } 303 // Prescriptions already linked to this medical record. 304 List<PrescriptionMedicalRecord> prescriptions = 305 prescriptionMedicalRecordRepository.findByMedicalRecordRecordId(medicalRecordId); 314 306 315 307 List<PrescriptionDTO> prescriptionDTOs = prescriptions.stream() … … 325 317 .collect(Collectors.toList()); 326 318 327 // Get selected allergies from linking table 328 List<ReportAllergy> reportAllergies = reportAllergyRepository.findByReportReportId(report.getReportId()); 329 List<MedicalRecordAllergies> allergies = new java.util.ArrayList<>(); 330 for (ReportAllergy ra : reportAllergies) { 331 // Find the corresponding MedicalRecordAllergies entry 332 List<MedicalRecordAllergies> mra = medicalRecordAllergyRepository.findByMedicalRecordRecordIdAndAllergyAllergyId( 333 medicalRecordId, ra.getAllergy().getAllergyId()); 334 allergies.addAll(mra); 335 } 319 // Allergies already linked to this medical record. 320 List<MedicalRecordAllergies> allergies = 321 medicalRecordAllergyRepository.findByMedicalRecordRecordId(medicalRecordId); 336 322 337 323 List<AllergyDTO> allergyDTOs = allergies.stream() … … 344 330 .collect(Collectors.toList()); 345 331 346 // Get selected symptoms from linking table 347 List<ReportSymptom> reportSymptoms = reportSymptomRepository.findByReportReportId(report.getReportId()); 348 List<MedicalRecordSymptoms> symptoms = new java.util.ArrayList<>(); 349 for (ReportSymptom rs : reportSymptoms) { 350 // Find the corresponding MedicalRecordSymptoms entry 351 List<MedicalRecordSymptoms> mrs = symptomRepository.findByMedicalRecordRecordIdAndSymptomSymptomId( 352 medicalRecordId, rs.getSymptom().getSymptomId()); 353 symptoms.addAll(mrs); 354 } 332 // Symptoms already linked to this medical record. 333 List<MedicalRecordSymptoms> symptoms = 334 symptomRepository.findByMedicalRecordRecordId(medicalRecordId); 355 335 356 336 List<SymptomDTO> symptomDTOs = symptoms.stream() -
backend/src/main/java/medora/service/PatientService.java
rae6cd79 r48d2bed 4 4 import medora.models.domain.Patient; 5 5 import medora.models.domain.MedicalRecord; 6 import medora.models.domain.User; 6 7 import medora.repository.PatientRepository; 7 8 import medora.repository.MedicalRecordRepository; 9 import medora.repository.UserRepository; 8 10 import org.slf4j.Logger; 9 11 import org.slf4j.LoggerFactory; 12 import org.springframework.security.crypto.password.PasswordEncoder; 10 13 import org.springframework.stereotype.Service; 11 14 import org.springframework.transaction.annotation.Transactional; … … 26 29 private final PatientRepository patientRepository; 27 30 private final MedicalRecordRepository medicalRecordRepository; 31 private final UserRepository userRepository; 32 private final PasswordEncoder passwordEncoder; 28 33 29 34 public PatientService(PatientRepository patientRepository, 30 MedicalRecordRepository medicalRecordRepository) { 35 MedicalRecordRepository medicalRecordRepository, 36 UserRepository userRepository, 37 PasswordEncoder passwordEncoder) { 31 38 this.patientRepository = patientRepository; 32 39 this.medicalRecordRepository = medicalRecordRepository; 40 this.userRepository = userRepository; 41 this.passwordEncoder = passwordEncoder; 33 42 } 34 43 … … 85 94 */ 86 95 @Transactional 87 public Patient createPatient(Patient patient ) {96 public Patient createPatient(Patient patient, String rawPassword) { 88 97 if (patient == null || patient.getEmbg() == null || patient.getEmbg().isBlank()) { 89 98 throw new IllegalArgumentException("Patient EMBG is required"); … … 95 104 throw new IllegalArgumentException("Patient last name is required"); 96 105 } 106 if (rawPassword == null || rawPassword.isBlank()) { 107 throw new IllegalArgumentException("Password is required"); 108 } 109 110 // Patients log in using their EMBG as username, matching the 111 // convention already established across the application. 112 if (userRepository.existsByUsername(patient.getEmbg())) { 113 throw new RuntimeException("A user account for this EMBG already exists"); 114 } 115 116 // Create the login account first (user_id is DB-generated), since 117 // Patient.user_id is a required foreign key pointing to it. 118 User user = new User(); 119 user.setUsername(patient.getEmbg()); 120 user.setPassword(passwordEncoder.encode(rawPassword)); 121 user.setRole("PATIENT"); 122 user.setFirstName(patient.getFirstName()); 123 user.setLastName(patient.getLastName()); 124 user.setIsActive(true); 125 User savedUser = userRepository.save(user); 126 127 patient.setUser(savedUser); 128 patient.setPatientId(patientRepository.findMaxPatientId() + 1); 97 129 98 130 logger.info("Creating new patient with EMBG: {}", patient.getEmbg()); … … 102 134 try { 103 135 MedicalRecord medicalRecord = new MedicalRecord(); 136 medicalRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1); 104 137 medicalRecord.setPatient(savedPatient); 105 138 medicalRecordRepository.save(medicalRecord); 106 139 logger.info("Created medical record for patient ID: {}", savedPatient.getPatientId()); 107 140 } catch (Exception e) { 108 logger.error("Failed to create medical record for patient: {}", e.getMessage()); 141 logger.error("Failed to create medical record for patient {}", savedPatient.getPatientId(), e); 142 throw e; 109 143 } 110 144 … … 156 190 if (!hasRecord) { 157 191 MedicalRecord medicalRecord = new MedicalRecord(); 192 medicalRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1); 158 193 medicalRecord.setPatient(patient); 159 194 medicalRecordRepository.save(medicalRecord); -
backend/src/main/java/medora/service/PrescriptionService.java
rae6cd79 r48d2bed 58 58 59 59 Prescriptions prescription = new Prescriptions(); 60 prescription.setPrescriptionId(prescriptionRepository.findMaxPrescriptionId() + 1); 60 61 prescription.setMedicationName(medicationName); 61 62 -
backend/src/main/java/medora/service/ProcedureService.java
rae6cd79 r48d2bed 27 27 private final MedicalRecordProcedureRepository medicalRecordProcedureRepository; 28 28 private final ProcedureResultRepository procedureResultRepository; 29 private final MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository;30 29 private final EntityManager entityManager; 31 30 private final BillingService billingService; … … 39 38 MedicalRecordProcedureRepository medicalRecordProcedureRepository, 40 39 ProcedureResultRepository procedureResultRepository, 41 MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository,42 40 EntityManager entityManager, 43 41 BillingService billingService) { … … 51 49 this.medicalRecordProcedureRepository = medicalRecordProcedureRepository; 52 50 this.procedureResultRepository = procedureResultRepository; 53 this.medicalRecordProcedureResultRepository = medicalRecordProcedureResultRepository;54 51 this.entityManager = entityManager; 55 52 this.billingService = billingService; … … 87 84 88 85 PerformedProcedures performed = new PerformedProcedures(); 86 performed.setPerformedId(performedProcedureRepository.findMaxPerformedId() + 1); 89 87 performed.setProcedure(procedure); 90 88 performed.setDoctor(doctor); … … 138 136 139 137 PerformedProcedures performed = new PerformedProcedures(); 138 performed.setPerformedId(performedProcedureRepository.findMaxPerformedId() + 1); 140 139 performed.setProcedure(procedure); 141 140 performed.setDoctor(doctor); … … 312 311 // Create new result 313 312 result = new ProcedureResults(); 313 Long nextResultId = procedureResultRepository.findMaxResultId() + 1; 314 result.setResultId(nextResultId); 314 315 result.setProcedure(procedure); 315 316 result.setResultDescription(resultDescription); 316 317 result.setResultDate(resultDate); 317 318 318 // Save result first to generate ID319 // Save result 319 320 ProcedureResults savedResult = procedureResultRepository.save(result); 320 321 321 // Then link to medical record 322 MedicalRecordProcedureResults link = new MedicalRecordProcedureResults(record, savedResult); 323 medicalRecordProcedureResultRepository.save(link); 322 // Ensure the catalog procedure is linked to this medical record, so the 323 // result becomes visible there (procedure_results only carries a 324 // procedure_id, not a record_id — the schema has no direct link between 325 // a medical record and a specific result, only between a medical record 326 // and the catalog procedure, matching the pattern validated for UseCase09). 327 boolean alreadyLinked = medicalRecordProcedureRepository 328 .existsByMedicalRecordRecordIdAndProcedureProcedureId(medicalRecordId, procedureId); 329 if (!alreadyLinked) { 330 medicalRecordProcedureRepository.linkProcedure(medicalRecordId, procedureId); 331 } 324 332 logger.info("Created new procedure result {} for medical record {}", savedResult.getResultId(), medicalRecordId); 325 333 return savedResult; -
backend/src/main/java/medora/service/ReferralService.java
rae6cd79 r48d2bed 88 88 89 89 Referrals referral = new Referrals(); 90 referral.setReferralId(referralRepository.findMaxReferralId() + 1); 90 91 referral.setMedicalRecord(medicalRecord); 91 92 referral.setFromDoctor(fromDoctor); … … 93 94 referral.setReason(reason); 94 95 referral.setReferralDate(referralDate); 95 referral.setAppointmentDate(appointmentDate);96 referral.setAppointmentTime(appointmentTime);97 96 98 97 logger.info("Creating referral for medical record ID: {} from doctor ID: {} to doctor ID: {}",
Note:
See TracChangeset
for help on using the changeset viewer.
