Ignore:
Timestamp:
09/24/26 12:09:29 (2 days ago)
Author:
MBK <marija.karapandzova@…>
Branches:
master
Parents:
ae6cd79
Message:

Remote database setup, fixed models and application properties

Location:
backend/src/main/java/medora
Files:
2 added
21 deleted
56 edited

Legend:

Unmodified
Added
Removed
  • backend/src/main/java/medora/controller/DoctorController.java

    rae6cd79 r48d2bed  
    6363                        .body(Map.of("error", "Email is required"));
    6464            }
     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            }
    6573
    6674            Doctors doctor = new Doctors();
    … …  
    8189            doctor.setDepartment(department);
    8290
    83             Doctors createdDoctor = doctorService.createDoctor(doctor);
     91            Doctors createdDoctor = doctorService.createDoctor(doctor, request.getUsername(), request.getPassword());
    8492            DoctorDTO dto = convertToDTO(createdDoctor);
    8593
  • backend/src/main/java/medora/controller/LabController.java

    rae6cd79 r48d2bed  
    125125    @PutMapping("/{testId}")
    126126    public ResponseEntity<?> updateLabTest(@PathVariable Long testId,
    127                                           @RequestBody CreateLabTestRequest request) {
     127                                           @RequestBody CreateLabTestRequest request) {
    128128        try {
    129129            logger.info("Updating lab test with ID: {}", testId);
    … …  
    164164            logger.info("Requesting lab test {} for patient {}", request.getTestId(), request.getPatientId());
    165165
     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
    166171            PerformedLabTests performedTest = labService.requestLabTestForPatient(
    167172                    request.getPatientId(),
    168173                    request.getDoctorId(),
    169174                    request.getTestId(),
     175                    request.getTechnicianId(),
    170176                    request.getTestDate(),
    171177                    request.getNotes()
  • backend/src/main/java/medora/controller/PatientController.java

    rae6cd79 r48d2bed  
    5757                        .body(Map.of("error", "EMBG is required"));
    5858            }
     59            if (request.getPassword() == null || request.getPassword().isBlank()) {
     60                return ResponseEntity.badRequest()
     61                        .body(Map.of("error", "Password is required"));
     62            }
    5963
    6064            Patient patient = new Patient();
    … …  
    6872            patient.setEmbg(request.getEmbg());
    6973
    70             Patient createdPatient = patientService.createPatient(patient);
     74            Patient createdPatient = patientService.createPatient(patient, request.getPassword());
    7175            PatientDTO dto = convertToDTO(createdPatient);
    7276
  • backend/src/main/java/medora/controller/ReferralController.java

    rae6cd79 r48d2bed  
    1313import jakarta.servlet.http.HttpServletRequest;
    1414
     15import java.time.LocalDate;
     16import java.time.LocalTime;
    1517import java.util.List;
    1618import java.util.Map;
    … …  
    8183            );
    8284
    83             ReferralDTO dto = convertToDTO(referral);
     85            ReferralDTO dto = convertToDTO(referral, request.getAppointmentDate(), request.getAppointmentTime());
    8486            return ResponseEntity.status(HttpStatus.CREATED).body(dto);
    8587        } catch (RuntimeException e) {
    … …  
    213215
    214216    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) {
    215226        String fromDoctorName = "";
    216227        Long fromDoctorId = null;
    … …  
    250261                referral.getReason(),
    251262                referral.getReferralDate(),
    252                 referral.getAppointmentDate(),
    253                 referral.getAppointmentTime()
     263                appointmentDate,
     264                appointmentTime
    254265        );
    255266    }
  • backend/src/main/java/medora/dto/CreateDoctorRequest.java

    rae6cd79 r48d2bed  
    1818    private Long specializationId;
    1919    private Long departmentId;
     20    private String username;
     21    private String password;
    2022}
  • backend/src/main/java/medora/dto/CreatePatientRequest.java

    rae6cd79 r48d2bed  
    2323    private String phoneNumber;
    2424    private String embg;
     25    private String password;
    2526}
  • backend/src/main/java/medora/dto/RequestLabTestRequest.java

    rae6cd79 r48d2bed  
    1616    private Long doctorId;
    1717    private Long testId;
     18    private Long technicianId;
    1819    private LocalDate testDate;
    1920    private String notes;
  • backend/src/main/java/medora/models/domain/Admin.java

    rae6cd79 r48d2bed  
    1515    private Long adminId;
    1616
     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
    1729    @OneToOne(optional = false)
    1830    @JoinColumn(name = "user_id", nullable = false, unique = true)
    1931    private User user;
    2032
    21     @Column(name = "permissions")
    22     private String permissions;
    23 
    2433    public Admin() {}
    2534
    26     public Admin(Long adminId, User user, String permissions) {
     35    public Admin(Long adminId, String username, String name, String lastname, String email, User user) {
    2736        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;
    3441        this.user = user;
    3542    }
  • backend/src/main/java/medora/models/domain/Appointment.java

    rae6cd79 r48d2bed  
    1616
    1717    @Id
    18     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "appointment_seq")
    19     @SequenceGenerator(name = "appointment_seq", sequenceName = "appointment_id_seq", allocationSize = 1)
    2018    @Column(name = "appointment_id")
    2119    private Long appointmentId;
  • backend/src/main/java/medora/models/domain/Billing.java

    rae6cd79 r48d2bed  
    1919
    2020    @Id
    21     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bill_seq")
    22     @SequenceGenerator(name = "bill_seq", sequenceName = "bill_id_seq", allocationSize = 1)
    2321    @Column(name = "bill_id")
    2422    private Long billId;
    … …  
    3937    private MedicalRecord medicalRecord;
    4038
    41     @ManyToOne(optional = true)
    42     @JoinColumn(name = "admin_id", nullable = true)
     39    @ManyToOne(optional = false)
     40    @JoinColumn(name = "admin_id", nullable = false)
    4341    private Admin admin;
    4442
  • backend/src/main/java/medora/models/domain/Diagnosis.java

    rae6cd79 r48d2bed  
    1212
    1313    @Id
    14     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "diagnosis_seq")
    15     @SequenceGenerator(name = "diagnosis_seq", sequenceName = "diagnosis_id_seq", allocationSize = 1)
    1614    @Column(name = "diagnosis_id")
    1715    private Long diagnosisId;
  • backend/src/main/java/medora/models/domain/Doctors.java

    rae6cd79 r48d2bed  
    1212
    1313    @Id
    14     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "doctor_seq")
    15     @SequenceGenerator(name = "doctor_seq", sequenceName = "doctor_id_seq", allocationSize = 1)
    1614    @Column(name = "doctor_id")
    1715    private Long doctorId;
    … …  
    3836    private Departments department;
    3937
     38    @OneToOne(fetch = FetchType.LAZY, optional = false)
     39    @JoinColumn(name = "user_id", nullable = false, unique = true)
     40    private User user;
     41
    4042    public Doctors() {}
    4143
    4244    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) {
    4447        this.doctorId = doctorId;
    4548        this.firstName = firstName;
    … …  
    4952        this.specialization = specialization;
    5053        this.department = department;
     54        this.user = user;
    5155    }
    5256}
  • backend/src/main/java/medora/models/domain/LabResults.java

    rae6cd79 r48d2bed  
    1515
    1616    @Id
    17     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "result_seq")
    18     @SequenceGenerator(name = "result_seq", sequenceName = "result_id_seq", allocationSize = 1)
    1917    @Column(name = "result_id")
    2018    private Long resultId;
  • backend/src/main/java/medora/models/domain/LabTechnician.java

    rae6cd79 r48d2bed  
    1515    private Long technicianId;
    1616
     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
    1729    @OneToOne(optional = false)
    1830    @JoinColumn(name = "user_id", nullable = false, unique = true)
    1931    private User user;
    2032
    21     @Column(name = "certification")
    22     private String certification;
    23 
    2433    public LabTechnician() {}
    2534
    26     public LabTechnician(Long technicianId, User user, String certification) {
     35    public LabTechnician(Long technicianId, String username, String name, String lastname, String email, User user) {
    2736        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;
    3441        this.user = user;
    3542    }
  • backend/src/main/java/medora/models/domain/LabTests.java

    rae6cd79 r48d2bed  
    1717
    1818    @Id
    19     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_seq")
    20     @SequenceGenerator(name = "test_seq", sequenceName = "test_id_seq", allocationSize = 1)
    2119    @Column(name = "test_id")
    2220    private Long testId;
  • backend/src/main/java/medora/models/domain/MedicalRecord.java

    rae6cd79 r48d2bed  
    1313
    1414    @Id
    15     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "record_seq")
    16     @SequenceGenerator(name = "record_seq", sequenceName = "record_id_seq", allocationSize = 1)
    1715    @Column(name = "record_id")
    1816    private Long recordId;
  • backend/src/main/java/medora/models/domain/MedicalReport.java

    rae6cd79 r48d2bed  
    1717
    1818    @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)
    2119    @Column(name = "report_id")
    2220    private Long reportId;
  • backend/src/main/java/medora/models/domain/Patient.java

    rae6cd79 r48d2bed  
    2020
    2121    @Id
    22     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "patient_seq")
    23     @SequenceGenerator(name = "patient_seq", sequenceName = "patient_id_seq", allocationSize = 1)
    2422    @Column(name = "patient_id")
    2523    private Long patientId;
    … …  
    5553    private List<MedicalRecord> medicalRecords;
    5654
     55    @OneToOne(fetch = FetchType.LAZY, optional = false)
     56    @JoinColumn(name = "user_id", nullable = false, unique = true)
     57    private User user;
     58
    5759    public Patient() {}
    5860
    … …  
    6567                   Gender gender,
    6668                   String phoneNumber,
    67                    String embg) {
     69                   String embg,
     70                   User user) {
    6871
    6972        this.patientId = patientId;
    … …  
    7679        this.phoneNumber = phoneNumber;
    7780        this.embg = embg;
     81        this.user = user;
    7882    }
    7983}
  • backend/src/main/java/medora/models/domain/PerformedLabTests.java

    rae6cd79 r48d2bed  
    1515
    1616    @Id
    17     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "performed_test_seq")
    18     @SequenceGenerator(name = "performed_test_seq", sequenceName = "performed_test_id_seq", allocationSize = 1)
    1917    @Column(name = "performed_test_id")
    2018    private Long performedTestId;
    … …  
    3230    private Doctors doctor;
    3331
    34     @ManyToOne(fetch = FetchType.LAZY)
    35     @JoinColumn(name = "technician_id")
     32    @ManyToOne(optional = false, fetch = FetchType.LAZY)
     33    @JoinColumn(name = "technician_id", nullable = false)
    3634    private LabTechnician technician;
    3735
  • backend/src/main/java/medora/models/domain/PerformedProcedures.java

    rae6cd79 r48d2bed  
    1414
    1515    @Id
    16     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "performed_id_seq")
    17     @SequenceGenerator(name = "performed_id_seq", sequenceName = "performed_procedures_performed_id_seq", allocationSize = 1)
    1816    @Column(name = "performed_id")
    1917    private Long performedId;
  • backend/src/main/java/medora/models/domain/Prescriptions.java

    rae6cd79 r48d2bed  
    1313
    1414    @Id
    15     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "prescription_seq")
    16     @SequenceGenerator(name = "prescription_seq", sequenceName = "prescription_id_seq", allocationSize = 1)
    1715    @Column(name = "prescription_id")
    1816    private Long prescriptionId;
  • backend/src/main/java/medora/models/domain/Procedure.java

    rae6cd79 r48d2bed  
    88
    99import java.math.BigDecimal;
     10import java.time.LocalDate;
    1011
    1112@Getter
    … …  
    1617
    1718    @Id
    18     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "procedure_seq")
    19     @SequenceGenerator(name = "procedure_seq", sequenceName = "procedure_id_seq", allocationSize = 1)
    2019    @Column(name = "procedure_id")
    2120    private Long procedureId;
    … …  
    2423    @Column(name = "procedure_type", nullable = false)
    2524    private String procedureType;
     25
     26    @Column(name = "procedure_date", nullable = false)
     27    private LocalDate procedureDate;
    2628
    2729    @Column(name = "description", columnDefinition = "TEXT")
    … …  
    3234    private BigDecimal cost;
    3335
     36    @ManyToOne(optional = false)
     37    @JoinColumn(name = "doctor_id", nullable = false)
     38    private Doctors doctor;
     39
    3440    public Procedure() {}
    3541
    3642    public Procedure(Long procedureId,
    3743                     String procedureType,
     44                     LocalDate procedureDate,
    3845                     String description,
    39                      BigDecimal cost) {
     46                     BigDecimal cost,
     47                     Doctors doctor) {
    4048
    4149        this.procedureId = procedureId;
    4250        this.procedureType = procedureType;
     51        this.procedureDate = procedureDate;
    4352        this.description = description;
    4453        this.cost = cost;
     54        this.doctor = doctor;
    4555    }
    4656}
  • backend/src/main/java/medora/models/domain/ProcedureResults.java

    rae6cd79 r48d2bed  
    1616
    1717    @Id
    18     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "result_seq")
    19     @SequenceGenerator(name = "result_seq", sequenceName = "procedure_results_result_id_seq", allocationSize = 1)
    2018    @Column(name = "result_id")
    2119    private Long resultId;
  • backend/src/main/java/medora/models/domain/Referrals.java

    rae6cd79 r48d2bed  
    88
    99import java.time.LocalDate;
    10 import java.time.LocalTime;
    1110
    1211@Getter
    … …  
    1716
    1817    @Id
    19     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "referral_seq")
    20     @SequenceGenerator(name = "referral_seq", sequenceName = "referral_id_seq", allocationSize = 1)
    2118    @Column(name = "referral_id")
    2219    private Long referralId;
    … …  
    2825    @Column(name = "referral_date", nullable = false)
    2926    private LocalDate referralDate;
    30 
    31     @Column(name = "appointment_date")
    32     private LocalDate appointmentDate;
    33 
    34     @Column(name = "appointment_time")
    35     private LocalTime appointmentTime;
    3627
    3728    @ManyToOne(optional = false)
    … …  
    5243                    String reason,
    5344                    LocalDate referralDate,
    54                     LocalDate appointmentDate,
    55                     LocalTime appointmentTime,
    5645                    MedicalRecord medicalRecord,
    5746                    Doctors fromDoctor,
    … …  
    6150        this.reason = reason;
    6251        this.referralDate = referralDate;
    63         this.appointmentDate = appointmentDate;
    64         this.appointmentTime = appointmentTime;
    6552        this.medicalRecord = medicalRecord;
    6653        this.fromDoctor = fromDoctor;
  • backend/src/main/java/medora/models/domain/User.java

    rae6cd79 r48d2bed  
    3434    private Boolean isActive = true;
    3535
    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 
    4636    public User() {}
    4737
  • backend/src/main/java/medora/models/enums/AppointmentStatus.java

    rae6cd79 r48d2bed  
    55    COMPLETED,
    66    CANCELLED,
    7     IN_PROGRESS
     7    IN_PROGRESS,
     8    NO_SHOW
    89}
  • backend/src/main/java/medora/repository/AdminRepository.java

    rae6cd79 r48d2bed  
    33import medora.models.domain.Admin;
    44import org.springframework.data.jpa.repository.JpaRepository;
     5import org.springframework.data.jpa.repository.Query;
    56
    67public 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();
    713}
    814
  • backend/src/main/java/medora/repository/AppointmentRepository.java

    rae6cd79 r48d2bed  
    1414
    1515public 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();
    1621
    1722    /**
  • backend/src/main/java/medora/repository/BillingLabTestsRepository.java

    rae6cd79 r48d2bed  
    1919    List<BillingLabTests> findByBillingBillId(@Param("billId") Long billId);
    2020
     21    boolean existsByBillingBillIdAndLabTestTestId(Long billId, Long testId);
     22
    2123    // Calculate total cost of lab tests for a billing record
    2224    @Query("""
    … …  
    2628    BigDecimal calculateTotalCostForBilling(@Param("billId") Long billId);
    2729}
    28 
  • backend/src/main/java/medora/repository/BillingProceduresRepository.java

    rae6cd79 r48d2bed  
    1919    List<BillingProcedures> findByBillingBillId(@Param("billId") Long billId);
    2020
     21    boolean existsByBillingBillIdAndProcedureProcedureId(Long billId, Long procedureId);
     22
    2123    // Calculate total cost of procedures for a billing record
    2224    @Query("""
    … …  
    2628    BigDecimal calculateTotalCostForBilling(@Param("billId") Long billId);
    2729}
    28 
  • backend/src/main/java/medora/repository/BillingRepository.java

    rae6cd79 r48d2bed  
    1313
    1414public 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();
    1520
    1621    // UC020 – Generate Billing Record
  • backend/src/main/java/medora/repository/DiagnosisRepository.java

    rae6cd79 r48d2bed  
    1010
    1111public 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
    1218//UC009 – Record Diagnosis
    1319   // A doctor adds a diagnosis to a patient’s medical record.
  • backend/src/main/java/medora/repository/DoctorRepository.java

    rae6cd79 r48d2bed  
    1111
    1212public 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);
    1322
    1423    // UC023 – View Departments (see DepartmentRepository)
  • backend/src/main/java/medora/repository/LabResultsRepository.java

    rae6cd79 r48d2bed  
    99
    1010public 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();
    1116
    1217    // UC014 – Store Lab Results
  • backend/src/main/java/medora/repository/LabTechnicianRepository.java

    rae6cd79 r48d2bed  
    33import medora.models.domain.LabTechnician;
    44import org.springframework.data.jpa.repository.JpaRepository;
     5import org.springframework.data.jpa.repository.Query;
    56
    67public 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();
    713}
  • backend/src/main/java/medora/repository/LabTestRepository.java

    rae6cd79 r48d2bed  
    1010
    1111public 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();
    1217
    1318    // UC013 – Record Lab Test Request
  • backend/src/main/java/medora/repository/MedicalRecordRepository.java

    rae6cd79 r48d2bed  
    1111
    1212public 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();
    1318
    1419    // UC005 – View Medical Record (Full history)
  • backend/src/main/java/medora/repository/MedicalReportRepository.java

    rae6cd79 r48d2bed  
    1515    // This properly manages entity lifecycle and relationships
    1616
     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
    1722    // UC005 – Retrieve medical reports for a medical record
    1823    List<MedicalReport> findByMedicalRecordRecordId(Long recordId);
  • backend/src/main/java/medora/repository/PatientRepository.java

    rae6cd79 r48d2bed  
    1010
    1111public 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);
    1221
    1322    // UC004 – View Patient Profile
  • backend/src/main/java/medora/repository/PerformedLabTestRepository.java

    rae6cd79 r48d2bed  
    1010
    1111public 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();
    1217
    1318    List<PerformedLabTests> findByPatientPatientId(Long patientId);
  • backend/src/main/java/medora/repository/PerformedProcedureRepository.java

    rae6cd79 r48d2bed  
    1313
    1414public 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();
    1520
    1621    // UC016 – Record Procedure Entry
  • backend/src/main/java/medora/repository/PrescriptionRepository.java

    rae6cd79 r48d2bed  
    1111
    1212public 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();
    1318
    1419    // UC012 – Record Prescription
  • backend/src/main/java/medora/repository/ProcedureRepository.java

    rae6cd79 r48d2bed  
    33import medora.models.domain.Procedure;
    44import org.springframework.data.jpa.repository.JpaRepository;
     5import org.springframework.data.jpa.repository.Query;
    56
    67public 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();
    713}
    814
  • backend/src/main/java/medora/repository/ProcedureResultRepository.java

    rae6cd79 r48d2bed  
    1010public interface ProcedureResultRepository extends JpaRepository<ProcedureResults, Long> {
    1111
     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
    1217    // UC017 – Record Procedure Outcome
    1318    List<ProcedureResults> findByProcedureProcedureId(Long procedureId);
    1419
    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.
    1625    @Query("""
    1726        SELECT pr FROM ProcedureResults pr
    18         WHERE pr IN (
    19             SELECT mrpr.procedureResult FROM MedicalRecordProcedureResults mrpr
    20             WHERE mrpr.medicalRecord.recordId = :recordId
     27        WHERE pr.procedure.procedureId IN (
     28            SELECT mrp.procedure.procedureId FROM MedicalRecordProcedures mrp
     29            WHERE mrp.medicalRecord.recordId = :recordId
    2130        )
    2231    """)
    … …  
    3342    @Query("""
    3443        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
    3848        )
    39         AND pr.procedure.procedureId = :procedureId
    4049    """)
    4150    List<ProcedureResults> findByMedicalRecordAndProcedure(@Param("recordId") Long recordId, @Param("procedureId") Long procedureId);
  • backend/src/main/java/medora/repository/ReferralRepository.java

    rae6cd79 r48d2bed  
    99
    1010public 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();
    1116
    1217    // UC019 – Create Referral Record
  • backend/src/main/java/medora/service/AppointmentService.java

    rae6cd79 r48d2bed  
    121121        Appointment appointment = new Appointment();
    122122
     123        appointment.setAppointmentId(appointmentRepository.findMaxAppointmentId() + 1);
    123124        appointment.setPatient(patient);
    124125        appointment.setDoctor(doctor);
  • backend/src/main/java/medora/service/AuthService.java

    rae6cd79 r48d2bed  
    33import medora.models.domain.User;
    44import medora.repository.UserRepository;
     5import medora.repository.DoctorRepository;
     6import medora.repository.PatientRepository;
    57import medora.util.JwtUtil;
    68import org.slf4j.Logger;
    … …  
    2123
    2224    private final UserRepository userRepository;
     25    private final DoctorRepository doctorRepository;
     26    private final PatientRepository patientRepository;
    2327    private final JwtUtil jwtUtil;
    2428    private final PasswordEncoder passwordEncoder;
    2529
    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) {
    2735        this.userRepository = userRepository;
     36        this.doctorRepository = doctorRepository;
     37        this.patientRepository = patientRepository;
    2838        this.jwtUtil = jwtUtil;
    2939        this.passwordEncoder = passwordEncoder;
    … …  
    5868        }
    5969
    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);
    6279        String token = jwtUtil.generateTokenWithDoctorId(user.getUsername(), user.getRole(), user.getUserId(), patientId, doctorId);
    6380
  • backend/src/main/java/medora/service/BillingService.java

    rae6cd79 r48d2bed  
    9191
    9292        Billing billing = new Billing();
     93        billing.setBillId(billingRepository.findMaxBillId() + 1);
    9394        billing.setMedicalRecord(medicalRecord);
     95        billing.setTotalCost(totalCost);
    9496        billing.setAdmin(admin);
    95         billing.setTotalCost(totalCost);
    9697        billing.setPaymentStatus(PaymentStatus.PENDING);
    97 
    98         logger.info("Generating billing record for medical record ID: {} with total cost: {}",
    99                 medicalRecordId, totalCost);
    10098        return billingRepository.save(billing);
    10199    }
    … …  
    292290                        logger.info("Creating new medical record for patient {}", patientId);
    293291                        MedicalRecord newRecord = new MedicalRecord();
     292                        newRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1);
    294293                        newRecord.setPatient(patientRepository.findById(patientId)
    295294                                .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + patientId)));
    … …  
    354353                // Create new billing record
    355354                billing = new Billing();
     355                billing.setBillId(billingRepository.findMaxBillId() + 1);
    356356                billing.setMedicalRecord(medicalRecord);
    357357                billing.setAdmin(admin);
    … …  
    369369            // Link procedures to billing (only if not already linked)
    370370            for (PerformedProcedures procedure : procedures) {
    371                 try {
     371                if (!billingProceduresRepository.existsByBillingBillIdAndProcedureProcedureId(
     372                        savedBilling.getBillId(), procedure.getProcedure().getProcedureId())) {
    372373                    BillingProcedures billingProcedure = new BillingProcedures(savedBilling, procedure.getProcedure());
    373374                    billingProceduresRepository.save(billingProcedure);
    374375                    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());
    377376                }
    378377            }
    … …  
    380379            // Link lab tests to billing (only if not already linked)
    381380            for (PerformedLabTests labTest : labTests) {
    382                 try {
     381                if (!billingLabTestsRepository.existsByBillingBillIdAndLabTestTestId(
     382                        savedBilling.getBillId(), labTest.getLabTest().getTestId())) {
    383383                    BillingLabTests billingLabTest = new BillingLabTests(savedBilling, labTest.getLabTest());
    384384                    billingLabTestsRepository.save(billingLabTest);
    385385                    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());
    388386                }
    389387            }
  • backend/src/main/java/medora/service/DiagnosisService.java

    rae6cd79 r48d2bed  
    6060
    6161        Diagnosis diagnosis = new Diagnosis();
     62        diagnosis.setDiagnosisId(diagnosisRepository.findMaxDiagnosisId() + 1);
    6263        diagnosis.setName(diagnosisName);
    6364        diagnosis.setDescription(description);
  • backend/src/main/java/medora/service/DoctorService.java

    rae6cd79 r48d2bed  
    33import medora.models.domain.Doctors;
    44import medora.models.domain.Departments;
     5import medora.models.domain.User;
    56import medora.repository.DoctorRepository;
    67import medora.repository.DepartmentRepository;
     8import medora.repository.UserRepository;
    79import org.slf4j.Logger;
    810import org.slf4j.LoggerFactory;
     11import org.springframework.security.crypto.password.PasswordEncoder;
    912import org.springframework.stereotype.Service;
    1013import org.springframework.transaction.annotation.Transactional;
    … …  
    2427    private final DoctorRepository doctorRepository;
    2528    private final DepartmentRepository departmentRepository;
     29    private final UserRepository userRepository;
     30    private final PasswordEncoder passwordEncoder;
    2631
    2732    public DoctorService(DoctorRepository doctorRepository,
    28                          DepartmentRepository departmentRepository) {
     33                         DepartmentRepository departmentRepository,
     34                         UserRepository userRepository,
     35                         PasswordEncoder passwordEncoder) {
    2936        this.doctorRepository = doctorRepository;
    3037        this.departmentRepository = departmentRepository;
     38        this.userRepository = userRepository;
     39        this.passwordEncoder = passwordEncoder;
    3140    }
    3241
    … …  
    119128     */
    120129    @Transactional
    121     public Doctors createDoctor(Doctors doctor) {
     130    public Doctors createDoctor(Doctors doctor, String username, String rawPassword) {
    122131
    123132        if (doctor == null) {
    … …  
    139148        if (doctor.getDepartment() == null || doctor.getDepartment().getDepartmentId() == null) {
    140149            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");
    141158        }
    142159
    … …  
    144161        if (doctorRepository.findByEmailAddress(doctor.getEmailAddress()).isPresent()) {
    145162            throw new RuntimeException("Doctor with this email already exists");
     163        }
     164
     165        if (userRepository.existsByUsername(username)) {
     166            throw new RuntimeException("Username already taken");
    146167        }
    147168
    … …  
    153174        doctor.setDepartment(department);
    154175
     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
    155190        logger.info("Creating new doctor: {} {}", doctor.getFirstName(), doctor.getLastName());
    156191
  • backend/src/main/java/medora/service/LabService.java

    rae6cd79 r48d2bed  
    6666
    6767        LabTests labTest = new LabTests();
     68        labTest.setTestId(labTestRepository.findMaxTestId() + 1);
    6869        labTest.setTestName(testName);
    6970        labTest.setDescription(description);
    … …  
    113114                                                      Long doctorId,
    114115                                                      Long testId,
     116                                                      Long technicianId,
    115117                                                      LocalDate testDate,
    116118                                                      String notes) {
    … …  
    124126            throw new IllegalArgumentException("Invalid test ID");
    125127
     128        if (technicianId == null || technicianId <= 0)
     129            throw new IllegalArgumentException("Invalid lab technician ID");
     130
    126131        Patient patient = patientRepository.findById(patientId)
    127132                .orElseThrow(() -> new RuntimeException("Patient not found"));
    … …  
    133138                .orElseThrow(() -> new RuntimeException("Lab test not found"));
    134139
     140        LabTechnician technician = labTechnicianRepository.findById(technicianId)
     141                .orElseThrow(() -> new RuntimeException("Lab technician not found"));
     142
    135143        PerformedLabTests performedTest = new PerformedLabTests();
     144        performedTest.setPerformedTestId(performedLabTestRepository.findMaxPerformedTestId() + 1);
    136145        performedTest.setPatient(patient);
    137146        performedTest.setDoctor(doctor);
    138147        performedTest.setLabTest(test);
     148        performedTest.setTechnician(technician);
    139149        LocalDate finalTestDate = testDate != null ? testDate : LocalDate.now();
    140150        performedTest.setTestDate(finalTestDate);
    … …  
    189199            throw new IllegalArgumentException("Results required");
    190200
     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
    191205        MedicalRecord medicalRecord = medicalRecordRepository.findById(medicalRecordId)
    192206                .orElseThrow(() -> new RuntimeException("Medical record not found"));
    … …  
    196210
    197211        LabResults labResult = new LabResults();
     212        labResult.setResultId(labResultsRepository.findMaxResultId() + 1);
    198213        labResult.setResults(results);
    199         labResult.setResultDate(resultDate);
     214        labResult.setResultDate(finalResultDate);
    200215        labResult.setLabTest(labTest);
    201216
  • backend/src/main/java/medora/service/MedicalReportService.java

    rae6cd79 r48d2bed  
    1010
    1111import java.time.LocalDate;
    12 import java.util.HashSet;
    1312import java.util.List;
    1413import java.util.Optional;
    15 import java.util.Set;
    1614import java.util.stream.Collectors;
    1715
    … …  
    2018 * UC018 – Create Medical Report
    2119 * 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.
    2229 */
    2330@Service
    … …  
    3340    private final MedicalRecordAllergyRepository medicalRecordAllergyRepository;
    3441    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;
    4242
    4343    public MedicalReportService(MedicalReportRepository medicalReportRepository,
    … …  
    4747                                PrescriptionMedicalRecordRepository prescriptionMedicalRecordRepository,
    4848                                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) {
    5750        this.medicalReportRepository = medicalReportRepository;
    5851        this.doctorRepository = doctorRepository;
    … …  
    6255        this.medicalRecordAllergyRepository = medicalRecordAllergyRepository;
    6356        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;
    7157    }
    7258
    7359    /**
    7460     * 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.
    7663     */
    7764    @Transactional
    … …  
    9885
    9986        MedicalReport report = new MedicalReport();
    100         // Let JPA auto-generate the ID using the sequence
    101         report.setReportId(null);
     87        Long nextReportId = medicalReportRepository.findMaxReportId() + 1;
     88        report.setReportId(nextReportId);
    10289        report.setDoctor(doctor);
    10390        report.setMedicalRecord(medicalRecord);
    … …  
    11198
    11299    /**
    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.
    114106     */
    115107    @Transactional
    … …  
    119111            List<Long> selectedAllergyIds, List<Long> selectedSymptomIds) {
    120112
    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
    125116        if (selectedDiagnosisIds != null) {
    126117            for (Long diagnosisId : selectedDiagnosisIds) {
    127                 Diagnosis diagnosis = diagnosisRepository.findById(diagnosisId)
     118                diagnosisRepository.findById(diagnosisId)
    128119                        .orElseThrow(() -> new RuntimeException("Diagnosis not found with ID: " + diagnosisId));
    129                 ReportDiagnosis reportDiagnosis = new ReportDiagnosis(report, diagnosis);
    130                 reportDiagnosisRepository.save(reportDiagnosis);
    131120            }
    132121        }
    133122
    134         // Store selected prescriptions
    135123        if (selectedPrescriptionIds != null) {
    136124            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                }
    141131            }
    142132        }
    143133
    144         // Store selected allergies
    145134        if (selectedAllergyIds != null) {
    146135            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                }
    151142            }
    152143        }
    153144
    154         // Store selected symptoms
    155145        if (selectedSymptomIds != null) {
    156146            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                }
    161153            }
    162154        }
    163155
    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);
    166164    }
    167165
    … …  
    279277
    280278    /**
    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).
    282282     */
    283283    private ComprehensiveMedicalReportDTO buildComprehensiveReport(MedicalReport report) {
    … …  
    285285        Patient patient = report.getMedicalRecord().getPatient();
    286286
    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());
    292290
    293291        List<DiagnosisDTO> diagnosisDTOs = diagnoses.stream()
    … …  
    303301                .collect(Collectors.toList());
    304302
    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);
    314306
    315307        List<PrescriptionDTO> prescriptionDTOs = prescriptions.stream()
    … …  
    325317                .collect(Collectors.toList());
    326318
    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);
    336322
    337323        List<AllergyDTO> allergyDTOs = allergies.stream()
    … …  
    344330                .collect(Collectors.toList());
    345331
    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);
    355335
    356336        List<SymptomDTO> symptomDTOs = symptoms.stream()
  • backend/src/main/java/medora/service/PatientService.java

    rae6cd79 r48d2bed  
    44import medora.models.domain.Patient;
    55import medora.models.domain.MedicalRecord;
     6import medora.models.domain.User;
    67import medora.repository.PatientRepository;
    78import medora.repository.MedicalRecordRepository;
     9import medora.repository.UserRepository;
    810import org.slf4j.Logger;
    911import org.slf4j.LoggerFactory;
     12import org.springframework.security.crypto.password.PasswordEncoder;
    1013import org.springframework.stereotype.Service;
    1114import org.springframework.transaction.annotation.Transactional;
    … …  
    2629    private final PatientRepository patientRepository;
    2730    private final MedicalRecordRepository medicalRecordRepository;
     31    private final UserRepository userRepository;
     32    private final PasswordEncoder passwordEncoder;
    2833
    2934    public PatientService(PatientRepository patientRepository,
    30                          MedicalRecordRepository medicalRecordRepository) {
     35                         MedicalRecordRepository medicalRecordRepository,
     36                         UserRepository userRepository,
     37                         PasswordEncoder passwordEncoder) {
    3138        this.patientRepository = patientRepository;
    3239        this.medicalRecordRepository = medicalRecordRepository;
     40        this.userRepository = userRepository;
     41        this.passwordEncoder = passwordEncoder;
    3342    }
    3443
    … …  
    8594     */
    8695    @Transactional
    87     public Patient createPatient(Patient patient) {
     96    public Patient createPatient(Patient patient, String rawPassword) {
    8897        if (patient == null || patient.getEmbg() == null || patient.getEmbg().isBlank()) {
    8998            throw new IllegalArgumentException("Patient EMBG is required");
    … …  
    95104            throw new IllegalArgumentException("Patient last name is required");
    96105        }
     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);
    97129
    98130        logger.info("Creating new patient with EMBG: {}", patient.getEmbg());
    … …  
    102134        try {
    103135            MedicalRecord medicalRecord = new MedicalRecord();
     136            medicalRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1);
    104137            medicalRecord.setPatient(savedPatient);
    105138            medicalRecordRepository.save(medicalRecord);
    106139            logger.info("Created medical record for patient ID: {}", savedPatient.getPatientId());
    107140        } 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;
    109143        }
    110144
    … …  
    156190                if (!hasRecord) {
    157191                    MedicalRecord medicalRecord = new MedicalRecord();
     192                    medicalRecord.setRecordId(medicalRecordRepository.findMaxRecordId() + 1);
    158193                    medicalRecord.setPatient(patient);
    159194                    medicalRecordRepository.save(medicalRecord);
  • backend/src/main/java/medora/service/PrescriptionService.java

    rae6cd79 r48d2bed  
    5858
    5959        Prescriptions prescription = new Prescriptions();
     60        prescription.setPrescriptionId(prescriptionRepository.findMaxPrescriptionId() + 1);
    6061        prescription.setMedicationName(medicationName);
    6162
  • backend/src/main/java/medora/service/ProcedureService.java

    rae6cd79 r48d2bed  
    2727    private final MedicalRecordProcedureRepository medicalRecordProcedureRepository;
    2828    private final ProcedureResultRepository procedureResultRepository;
    29     private final MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository;
    3029    private final EntityManager entityManager;
    3130    private final BillingService billingService;
    … …  
    3938                            MedicalRecordProcedureRepository medicalRecordProcedureRepository,
    4039                            ProcedureResultRepository procedureResultRepository,
    41                             MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository,
    4240                            EntityManager entityManager,
    4341                            BillingService billingService) {
    … …  
    5149        this.medicalRecordProcedureRepository = medicalRecordProcedureRepository;
    5250        this.procedureResultRepository = procedureResultRepository;
    53         this.medicalRecordProcedureResultRepository = medicalRecordProcedureResultRepository;
    5451        this.entityManager = entityManager;
    5552        this.billingService = billingService;
    … …  
    8784
    8885        PerformedProcedures performed = new PerformedProcedures();
     86        performed.setPerformedId(performedProcedureRepository.findMaxPerformedId() + 1);
    8987        performed.setProcedure(procedure);
    9088        performed.setDoctor(doctor);
    … …  
    138136
    139137        PerformedProcedures performed = new PerformedProcedures();
     138        performed.setPerformedId(performedProcedureRepository.findMaxPerformedId() + 1);
    140139        performed.setProcedure(procedure);
    141140        performed.setDoctor(doctor);
    … …  
    312311            // Create new result
    313312            result = new ProcedureResults();
     313            Long nextResultId = procedureResultRepository.findMaxResultId() + 1;
     314            result.setResultId(nextResultId);
    314315            result.setProcedure(procedure);
    315316            result.setResultDescription(resultDescription);
    316317            result.setResultDate(resultDate);
    317318
    318             // Save result first to generate ID
     319            // Save result
    319320            ProcedureResults savedResult = procedureResultRepository.save(result);
    320321
    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            }
    324332            logger.info("Created new procedure result {} for medical record {}", savedResult.getResultId(), medicalRecordId);
    325333            return savedResult;
  • backend/src/main/java/medora/service/ReferralService.java

    rae6cd79 r48d2bed  
    8888
    8989        Referrals referral = new Referrals();
     90        referral.setReferralId(referralRepository.findMaxReferralId() + 1);
    9091        referral.setMedicalRecord(medicalRecord);
    9192        referral.setFromDoctor(fromDoctor);
    … …  
    9394        referral.setReason(reason);
    9495        referral.setReferralDate(referralDate);
    95         referral.setAppointmentDate(appointmentDate);
    96         referral.setAppointmentTime(appointmentTime);
    9796
    9897        logger.info("Creating referral for medical record ID: {} from doctor ID: {} to doctor ID: {}",
Note: See TracChangeset for help on using the changeset viewer.