Changeset a95bd1b for backend/src


Ignore:
Timestamp:
05/23/26 14:21:13 (4 months ago)
Author:
MBK <marija.karapandzova@…>
Branches:
master
Children:
7e4a5fd
Parents:
0e894d2
Message:

Add functionalities for total sum in billing and generate pdf invoice for a bill

Location:
backend/src/main/java/medora
Files:
3 added
9 edited

Legend:

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

    r0e894d2 ra95bd1b  
    22
    33import medora.dto.BillingDTO;
     4import medora.dto.BillingDetailDTO;
    45import medora.dto.CreateBillingRequest;
    56import medora.dto.UpdateBillingRequest;
    67import medora.models.domain.Billing;
     8import medora.models.enums.PaymentStatus;
    79import medora.service.BillingService;
     10import medora.util.BillingPDFGenerator;
    811import org.slf4j.Logger;
    912import org.slf4j.LoggerFactory;
     13import org.springframework.http.HttpHeaders;
    1014import org.springframework.http.HttpStatus;
     15import org.springframework.http.MediaType;
    1116import org.springframework.http.ResponseEntity;
    1217import org.springframework.web.bind.annotation.*;
     
    8287    }
    8388
     89    @GetMapping("/{billId}/detail")
     90    public ResponseEntity<?> getBillingDetail(@PathVariable Long billId) {
     91        try {
     92            logger.info("Fetching detailed billing information for bill ID: {}", billId);
     93            BillingDetailDTO detail = billingService.getBillingDetail(billId);
     94            return ResponseEntity.ok(detail);
     95        } catch (RuntimeException e) {
     96            logger.error("Error fetching billing detail: {}", e.getMessage());
     97            return ResponseEntity.badRequest()
     98                    .body(Map.of("error", e.getMessage()));
     99        } catch (Exception e) {
     100            logger.error("Unexpected error fetching billing detail: {}", e.getMessage());
     101            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
     102                    .body(Map.of("error", "Failed to fetch billing detail: " + e.getMessage()));
     103        }
     104    }
     105
    84106    @GetMapping
    85107    public ResponseEntity<?> getAllBillings() {
     
    124146    @PatchMapping("/{billId}/payment-status")
    125147    public ResponseEntity<?> updatePaymentStatus(@PathVariable Long billId,
    126                                                 @RequestBody UpdateBillingRequest request) {
     148                                                 @RequestBody UpdateBillingRequest request) {
    127149        try {
    128150            if (request.getPaymentStatus() == null) {
     
    144166            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
    145167                    .body(Map.of("error", "Failed to update payment status: " + e.getMessage()));
     168        }
     169    }
     170
     171    @GetMapping("/{billId}/invoice-pdf")
     172    public ResponseEntity<?> downloadInvoicePDF(@PathVariable Long billId) {
     173        try {
     174            logger.info("Generating PDF invoice for bill ID: {}", billId);
     175            BillingDetailDTO billingDetail = billingService.getBillingDetail(billId);
     176            byte[] pdfContent = BillingPDFGenerator.generateInvoicePDF(billingDetail);
     177
     178            HttpHeaders headers = new HttpHeaders();
     179            headers.setContentType(MediaType.APPLICATION_PDF);
     180            headers.setContentDispositionFormData("attachment", "invoice-" + billId + ".pdf");
     181            headers.setContentLength(pdfContent.length);
     182
     183            return new ResponseEntity<>(pdfContent, headers, HttpStatus.OK);
     184        } catch (RuntimeException e) {
     185            logger.error("Error generating PDF invoice: {}", e.getMessage());
     186            return ResponseEntity.badRequest()
     187                    .body(Map.of("error", e.getMessage()));
     188        } catch (Exception e) {
     189            logger.error("Unexpected error generating PDF invoice: {}", e.getMessage());
     190            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
     191                    .body(Map.of("error", "Failed to generate invoice: " + e.getMessage()));
    146192        }
    147193    }
     
    151197        if (billing.getMedicalRecord() != null && billing.getMedicalRecord().getPatient() != null) {
    152198            patientName = billing.getMedicalRecord().getPatient().getFirstName() + " " +
    153                          billing.getMedicalRecord().getPatient().getLastName();
     199                    billing.getMedicalRecord().getPatient().getLastName();
    154200        }
    155201        return new BillingDTO(
  • backend/src/main/java/medora/repository/BillingRepository.java

    r0e894d2 ra95bd1b  
    7979    """)
    8080    List<Billing> findBillingHistoryForPatient(@Param("patientId") Long patientId);
     81
     82    // UC020 – Auto billing: Check if billing exists for patient on a specific date
     83    @Query(value = """
     84        SELECT COUNT(b.bill_id) > 0
     85        FROM billing b
     86        JOIN medical_records mr ON b.record_id = mr.record_id
     87        WHERE mr.patient_id = :patientId
     88        AND CAST(b.payment_date AS DATE) = :billDate
     89    """, nativeQuery = true)
     90    boolean existsBillingForPatientOnDate(@Param("patientId") Long patientId, @Param("billDate") java.time.LocalDate billDate);
     91
     92    // UC020 – Auto billing: Get billing for patient on a specific date
     93    @Query(value = """
     94        SELECT b.* FROM billing b
     95        JOIN medical_records mr ON b.record_id = mr.record_id
     96        WHERE mr.patient_id = :patientId
     97        AND CAST(b.payment_date AS DATE) = :billDate
     98        LIMIT 1
     99    """, nativeQuery = true)
     100    Billing findBillingForPatientOnDate(@Param("patientId") Long patientId, @Param("billDate") java.time.LocalDate billDate);
     101
     102    // UC020 – Auto billing: Calculate total cost for patient on a specific date (using view)
     103    @Query(value = """
     104        SELECT COALESCE(total_cost, 0::decimal)
     105        FROM daily_patient_billing_totals
     106        WHERE patient_id = :patientId
     107        AND service_date = :serviceDate
     108    """, nativeQuery = true)
     109    BigDecimal calculateDailyTotalCostForPatient(@Param("patientId") Long patientId, @Param("serviceDate") java.time.LocalDate serviceDate);
     110
     111    // UC020 – Get procedures for a billing record
     112    @Query(value = """
     113        SELECT p.procedure_id, p.procedure_type, p.cost
     114        FROM billing_procedures bp
     115        JOIN procedures p ON bp.procedure_id = p.procedure_id
     116        WHERE bp.bill_id = :billId
     117    """, nativeQuery = true)
     118    java.util.List<Object[]> findProceduresForBilling(@Param("billId") Long billId);
     119
     120    // UC020 – Get lab tests for a billing record
     121    @Query(value = """
     122        SELECT l.test_id, l.test_name, l.cost
     123        FROM billing_lab_tests blt
     124        JOIN lab_tests l ON blt.test_id = l.test_id
     125        WHERE blt.bill_id = :billId
     126    """, nativeQuery = true)
     127    java.util.List<Object[]> findLabTestsForBilling(@Param("billId") Long billId);
    81128}
  • backend/src/main/java/medora/repository/PerformedLabTestRepository.java

    r0e894d2 ra95bd1b  
    44import medora.models.domain.PerformedLabTests;
    55import org.springframework.data.jpa.repository.JpaRepository;
     6import org.springframework.data.jpa.repository.Query;
     7import org.springframework.data.repository.query.Param;
    68
    79import java.util.List;
     
    1214
    1315    List<PerformedLabTests> findByDoctorDoctorId(Long doctorId);
     16
     17    // UC020 – Auto billing: Get lab tests for a patient on a specific date
     18    @Query("""
     19        SELECT plt FROM PerformedLabTests plt
     20        WHERE plt.patient.patientId = :patientId
     21        AND plt.testDate = :testDate
     22    """)
     23    List<PerformedLabTests> findByPatientAndDate(@Param("patientId") Long patientId, @Param("testDate") java.time.LocalDate testDate);
    1424}
  • backend/src/main/java/medora/repository/PerformedProcedureRepository.java

    r0e894d2 ra95bd1b  
    4545    """)
    4646    List<PerformedProcedures> findByMedicalRecordId(@Param("recordId") Long recordId);
     47
     48    // UC020 – Auto billing: Get procedures for a patient on a specific date
     49    @Query("""
     50        SELECT pp FROM PerformedProcedures pp
     51        WHERE pp.patient.patientId = :patientId
     52        AND pp.procedureDate = :procedureDate
     53    """)
     54    List<PerformedProcedures> findByPatientAndDate(@Param("patientId") Long patientId, @Param("procedureDate") java.time.LocalDate procedureDate);
    4755}
  • backend/src/main/java/medora/service/AppointmentService.java

    r0e894d2 ra95bd1b  
    22
    33import medora.models.domain.Appointment;
     4import medora.models.domain.Patient;
    45import medora.models.domain.Doctors;
    5 import medora.models.domain.Patient;
    66import medora.models.enums.AppointmentStatus;
    77import medora.repository.AppointmentRepository;
     8import medora.repository.PatientRepository;
    89import medora.repository.DoctorRepository;
    9 import medora.repository.PatientRepository;
     10
    1011import org.slf4j.Logger;
    1112import org.slf4j.LoggerFactory;
     13
    1214import org.springframework.stereotype.Service;
    1315import org.springframework.transaction.annotation.Transactional;
     
    7981                LocalDateTime.of(appointmentDate, appointmentTime);
    8082
    81 
     83        // Future validation
    8284        if (!appointmentDateTime.isAfter(LocalDateTime.now())) {
    8385            throw new RuntimeException(
     
    8688        }
    8789
     90        // Doctor slot validation
    8891        boolean doctorBusy =
    8992                appointmentRepository
     
    99102        }
    100103
     104        // Duplicate patient validation
    101105        boolean duplicateAppointment =
    102106                appointmentRepository
     
    204208    }
    205209
    206 
    207210    @Transactional(readOnly = true)
    208211    public Optional<Appointment> getAppointmentById(Long appointmentId) {
     
    217220    }
    218221
    219     /**
    220      * Get appointments for patient
    221      */
     222
    222223    @Transactional(readOnly = true)
    223224    public List<Appointment> getAppointmentsForPatient(Long patientId) {
     
    297298        return appointmentRepository.findAll();
    298299    }
     300
     301
     302    public LocalTime findNextAvailableSlot(Long doctorId, LocalDate appointmentDate) {
     303        LocalTime[] timeSlots = {
     304                LocalTime.of(9, 0),
     305                LocalTime.of(10, 0),
     306                LocalTime.of(11, 0),
     307                LocalTime.of(13, 0),
     308                LocalTime.of(14, 0),
     309                LocalTime.of(15, 0),
     310                LocalTime.of(16, 0)
     311        };
     312
     313        for (LocalTime timeSlot : timeSlots) {
     314            boolean isBooked = appointmentRepository
     315                    .existsByDoctorDoctorIdAndAppointmentDateAndAppointmentTimeAndStatusNot(
     316                            doctorId,
     317                            appointmentDate,
     318                            timeSlot,
     319                            AppointmentStatus.CANCELLED
     320                    );
     321            if (!isBooked) {
     322                return timeSlot;
     323            }
     324        }
     325        return null;
     326    }
    299327}
  • backend/src/main/java/medora/service/BillingService.java

    r0e894d2 ra95bd1b  
    11package medora.service;
    22
    3 import medora.models.domain.*;
     3import medora.models.domain.Billing;
     4import medora.models.domain.MedicalRecord;
     5import medora.models.domain.Admin;
     6import medora.models.domain.BillingLabTests;
     7import medora.models.domain.BillingProcedures;
     8import medora.models.domain.PerformedProcedures;
     9import medora.models.domain.PerformedLabTests;
    410import medora.models.enums.PaymentStatus;
    5 import medora.repository.*;
     11import medora.repository.BillingRepository;
     12import medora.repository.MedicalRecordRepository;
     13import medora.repository.AdminRepository;
     14import medora.repository.BillingLabTestsRepository;
     15import medora.repository.BillingProceduresRepository;
     16import medora.repository.PerformedProcedureRepository;
     17import medora.repository.PerformedLabTestRepository;
     18import medora.repository.PatientRepository;
     19import medora.dto.BillingDetailDTO;
     20import medora.dto.BillingItemDTO;
    621import org.slf4j.Logger;
    722import org.slf4j.LoggerFactory;
     
    1328import java.util.List;
    1429import java.util.Optional;
     30import java.util.ArrayList;
    1531
    1632/**
     
    3046    private final BillingLabTestsRepository billingLabTestsRepository;
    3147    private final BillingProceduresRepository billingProceduresRepository;
     48    private final PerformedProcedureRepository performedProcedureRepository;
     49    private final PerformedLabTestRepository performedLabTestRepository;
     50    private final PatientRepository patientRepository;
    3251
    3352    public BillingService(BillingRepository billingRepository,
     
    3554                          AdminRepository adminRepository,
    3655                          BillingLabTestsRepository billingLabTestsRepository,
    37                           BillingProceduresRepository billingProceduresRepository) {
     56                          BillingProceduresRepository billingProceduresRepository,
     57                          PerformedProcedureRepository performedProcedureRepository,
     58                          PerformedLabTestRepository performedLabTestRepository,
     59                          PatientRepository patientRepository) {
    3860        this.billingRepository = billingRepository;
    3961        this.medicalRecordRepository = medicalRecordRepository;
     
    4163        this.billingLabTestsRepository = billingLabTestsRepository;
    4264        this.billingProceduresRepository = billingProceduresRepository;
     65        this.performedProcedureRepository = performedProcedureRepository;
     66        this.performedLabTestRepository = performedLabTestRepository;
     67        this.patientRepository = patientRepository;
    4368    }
    4469
     
    196221        }
    197222
    198         logger.info("Total billing cost for bill ID {}: procedures={}, lab tests={}", 
     223        logger.info("Total billing cost for bill ID {}: procedures={}, lab tests={}",
    199224                billId, procedureCost, labTestCost);
    200225        return procedureCost.add(labTestCost);
     
    219244        // This is a placeholder - adjust based on your actual Procedure entity
    220245        logger.info("Adding procedure {} to billing record {}", procedureId, billId);
    221        
     246
    222247        return null; // Will be implemented with ProcedureRepository injection
    223248    }
     
    241266        // This is a placeholder - adjust based on your actual LabTests entity
    242267        logger.info("Adding lab test {} to billing record {}", testId, billId);
    243        
     268
    244269        return null; // Will be implemented with LabTestRepository injection
    245270    }
     271
     272    /**
     273     * UC020 – Auto-generate billing when a procedure or lab test is performed
     274     * Creates a billing record if one doesn't exist for the patient on that date
     275     * Calculates total cost from all procedures and lab tests performed that day
     276     */
     277    @Transactional
     278    public void autoGenerateBillingForPatientService(Long patientId, LocalDate serviceDate) {
     279        try {
     280            if (patientId == null || patientId <= 0) {
     281                throw new IllegalArgumentException("Patient ID must be valid");
     282            }
     283            if (serviceDate == null) {
     284                throw new IllegalArgumentException("Service date must be valid");
     285            }
     286
     287            logger.info("Starting auto-billing for patient {} on date {}", patientId, serviceDate);
     288
     289            // Get patient's medical record (or create one if it doesn't exist)
     290            MedicalRecord medicalRecord = medicalRecordRepository.findByPatientPatientId(patientId)
     291                    .orElseGet(() -> {
     292                        logger.info("Creating new medical record for patient {}", patientId);
     293                        MedicalRecord newRecord = new MedicalRecord();
     294                        newRecord.setPatient(patientRepository.findById(patientId)
     295                                .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + patientId)));
     296                        return medicalRecordRepository.save(newRecord);
     297                    });
     298
     299            logger.info("Using medical record {} for patient {}", medicalRecord.getRecordId(), patientId);
     300
     301            // Get all procedures and lab tests for the patient on that date
     302            List<PerformedProcedures> procedures = performedProcedureRepository.findByPatientAndDate(patientId, serviceDate);
     303            List<PerformedLabTests> labTests = performedLabTestRepository.findByPatientAndDate(patientId, serviceDate);
     304
     305            logger.info("Found {} procedures and {} lab tests for patient {} on {}",
     306                    procedures.size(), labTests.size(), patientId, serviceDate);
     307
     308            // Only generate billing if there are procedures or lab tests on that date
     309            if (procedures.isEmpty() && labTests.isEmpty()) {
     310                logger.info("No procedures or lab tests found for patient {} on {}", patientId, serviceDate);
     311                return;
     312            }
     313
     314            // Calculate total cost
     315            BigDecimal procedureCost = procedures.stream()
     316                    .map(p -> {
     317                        BigDecimal cost = p.getProcedure().getCost();
     318                        logger.debug("Procedure {} cost: {}", p.getProcedure().getProcedureId(), cost);
     319                        return cost;
     320                    })
     321                    .reduce(BigDecimal.ZERO, BigDecimal::add);
     322
     323            BigDecimal labTestCost = labTests.stream()
     324                    .map(lt -> {
     325                        BigDecimal cost = lt.getLabTest().getCost();
     326                        logger.debug("Lab test {} cost: {}", lt.getLabTest().getTestId(), cost);
     327                        return cost;
     328                    })
     329                    .reduce(BigDecimal.ZERO, BigDecimal::add);
     330
     331            BigDecimal totalCost = procedureCost.add(labTestCost);
     332            logger.info("Total cost calculation: procedures={}, labTests={}, total={}", procedureCost, labTestCost, totalCost);
     333
     334            // Check if billing already exists for this patient on this date
     335            Billing billing = billingRepository.findBillingForPatientOnDate(patientId, serviceDate);
     336
     337            if (billing != null) {
     338                logger.info("Billing record {} already exists for patient {} on {}, updating with new total",
     339                        billing.getBillId(), patientId, serviceDate);
     340                billing.setTotalCost(totalCost);
     341            } else {
     342                // Get default admin (first admin in system)
     343                Admin admin = adminRepository.findAll()
     344                        .stream()
     345                        .findFirst()
     346                        .orElseThrow(() -> new RuntimeException("No admin found in system"));
     347
     348                // Create new billing record
     349                billing = new Billing();
     350                billing.setMedicalRecord(medicalRecord);
     351                billing.setAdmin(admin);
     352                billing.setTotalCost(totalCost);
     353                billing.setPaymentStatus(PaymentStatus.PENDING);
     354                billing.setPaymentDate(serviceDate);
     355
     356                logger.info("Creating new billing record for patient {} on {}", patientId, serviceDate);
     357            }
     358
     359            Billing savedBilling = billingRepository.save(billing);
     360            logger.info("Billing record {} for patient {} on {} with total cost: {}",
     361                    savedBilling.getBillId(), patientId, serviceDate, totalCost);
     362
     363            // Link procedures to billing (only if not already linked)
     364            for (PerformedProcedures procedure : procedures) {
     365                try {
     366                    BillingProcedures billingProcedure = new BillingProcedures(savedBilling, procedure.getProcedure());
     367                    billingProceduresRepository.save(billingProcedure);
     368                    logger.debug("Linked procedure {} to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId());
     369                } catch (Exception e) {
     370                    logger.debug("Procedure {} already linked to billing {}", procedure.getProcedure().getProcedureId(), savedBilling.getBillId());
     371                }
     372            }
     373
     374            // Link lab tests to billing (only if not already linked)
     375            for (PerformedLabTests labTest : labTests) {
     376                try {
     377                    BillingLabTests billingLabTest = new BillingLabTests(savedBilling, labTest.getLabTest());
     378                    billingLabTestsRepository.save(billingLabTest);
     379                    logger.debug("Linked lab test {} to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId());
     380                } catch (Exception e) {
     381                    logger.debug("Lab test {} already linked to billing {}", labTest.getLabTest().getTestId(), savedBilling.getBillId());
     382                }
     383            }
     384
     385            logger.info("Successfully processed {} procedures and {} lab tests for billing record {}",
     386                    procedures.size(), labTests.size(), savedBilling.getBillId());
     387
     388        } catch (Exception e) {
     389            logger.error("Error in auto-billing for patient {} on date {}: {}", patientId, serviceDate, e.getMessage(), e);
     390            throw e;
     391        }
     392    }
     393
     394    /**
     395     * Get detailed billing information with itemized procedures and lab tests
     396     */
     397    @Transactional(readOnly = true)
     398    public BillingDetailDTO getBillingDetail(Long billId) {
     399        if (billId == null || billId <= 0) {
     400            throw new IllegalArgumentException("Bill ID must be valid");
     401        }
     402
     403        Billing billing = billingRepository.findById(billId)
     404                .orElseThrow(() -> new RuntimeException("Billing record not found with ID: " + billId));
     405
     406        // Get procedures for this bill
     407        List<Object[]> procedureResults = billingRepository.findProceduresForBilling(billId);
     408        List<BillingItemDTO> procedures = new ArrayList<>();
     409        for (Object[] row : procedureResults) {
     410            procedures.add(new BillingItemDTO(
     411                    ((Number) row[0]).longValue(),
     412                    (String) row[1],
     413                    (BigDecimal) row[2]
     414            ));
     415        }
     416
     417        // Get lab tests for this bill
     418        List<Object[]> labTestResults = billingRepository.findLabTestsForBilling(billId);
     419        List<BillingItemDTO> labTests = new ArrayList<>();
     420        for (Object[] row : labTestResults) {
     421            labTests.add(new BillingItemDTO(
     422                    ((Number) row[0]).longValue(),
     423                    (String) row[1],
     424                    (BigDecimal) row[2]
     425            ));
     426        }
     427
     428        // Build the detail DTO
     429        BillingDetailDTO detail = new BillingDetailDTO();
     430        detail.setBillId(billing.getBillId());
     431        detail.setPatientName(billing.getMedicalRecord().getPatient().getFirstName() + " " +
     432                billing.getMedicalRecord().getPatient().getLastName());
     433        detail.setPatientEmbg(billing.getMedicalRecord().getPatient().getEmbg());
     434        detail.setPatientPhone(billing.getMedicalRecord().getPatient().getPhoneNumber());
     435        detail.setTotalCost(billing.getTotalCost());
     436        detail.setPaymentStatus(billing.getPaymentStatus().toString());
     437        detail.setPaymentDate(billing.getPaymentDate());
     438        detail.setBillDate(billing.getPaymentDate());
     439        detail.setProcedures(procedures);
     440        detail.setLabTests(labTests);
     441
     442        logger.info("Retrieved detailed billing information for bill {}", billId);
     443        return detail;
     444    }
    246445}
  • backend/src/main/java/medora/service/LabService.java

    r0e894d2 ra95bd1b  
    3131    private final DoctorRepository doctorRepository;
    3232    private final LabTechnicianRepository labTechnicianRepository;
     33    private final BillingService billingService;
    3334
    3435    public LabService(LabTestRepository labTestRepository,
     
    3940                      PatientRepository patientRepository,
    4041                      DoctorRepository doctorRepository,
    41                       LabTechnicianRepository labTechnicianRepository) {
     42                      LabTechnicianRepository labTechnicianRepository,
     43                      BillingService billingService) {
    4244        this.labTestRepository = labTestRepository;
    4345        this.labResultsRepository = labResultsRepository;
     
    4850        this.doctorRepository = doctorRepository;
    4951        this.labTechnicianRepository = labTechnicianRepository;
    50     }
    51 
    52     // LAB TEST
     52        this.billingService = billingService;
     53    }
     54
     55    // ================= LAB TEST =================
    5356
    5457    @Transactional
     
    104107    }
    105108
    106     //  LAB TEST REQUESTS (UC013)
     109    // ================= LAB TEST REQUESTS (UC013) =================
    107110
    108111    @Transactional
     
    134137        performedTest.setDoctor(doctor);
    135138        performedTest.setLabTest(test);
    136         performedTest.setTestDate(testDate != null ? testDate : LocalDate.now());
     139        LocalDate finalTestDate = testDate != null ? testDate : LocalDate.now();
     140        performedTest.setTestDate(finalTestDate);
    137141        performedTest.setNotes(notes);
    138142
    139143        logger.info("Lab test {} requested for patient {} by doctor {}", testId, patientId, doctorId);
    140         return performedLabTestRepository.save(performedTest);
     144        PerformedLabTests saved = performedLabTestRepository.save(performedTest);
     145
     146        // Auto-generate billing for the patient on this date
     147        billingService.autoGenerateBillingForPatientService(patientId, finalTestDate);
     148
     149        return saved;
    141150    }
    142151
     
    163172    }
    164173
    165     //  LAB RESULTS (UC014, UC015)
     174    // ================= LAB RESULTS (UC014, UC015) =================
    166175
    167176    @Transactional
     
    193202        LabResults saved = labResultsRepository.save(labResult);
    194203
    195 
     204        // Use the join repository to safely link (avoid deleting existing links)
     205        // Create and save the join entity explicitly
    196206        MedicalRecordLabResults link = new MedicalRecordLabResults();
    197207        link.setMedicalRecord(medicalRecord);
  • backend/src/main/java/medora/service/ProcedureService.java

    r0e894d2 ra95bd1b  
    2929    private final MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository;
    3030    private final EntityManager entityManager;
     31    private final BillingService billingService;
    3132
    3233    public ProcedureService(PerformedProcedureRepository performedProcedureRepository,
     
    3940                            ProcedureResultRepository procedureResultRepository,
    4041                            MedicalRecordProcedureResultRepository medicalRecordProcedureResultRepository,
    41                             EntityManager entityManager) {
     42                            EntityManager entityManager,
     43                            BillingService billingService) {
    4244
    4345        this.performedProcedureRepository = performedProcedureRepository;
     
    5153        this.medicalRecordProcedureResultRepository = medicalRecordProcedureResultRepository;
    5254        this.entityManager = entityManager;
     55        this.billingService = billingService;
    5356    }
    5457
     
    97100
    98101        logger.info("Requested procedure {} for patient {} by doctor {}", procedureId, patientId, doctorId);
    99         return performedProcedureRepository.saveAndFlush(performed);
     102        PerformedProcedures saved = performedProcedureRepository.saveAndFlush(performed);
     103
     104        // Auto-generate billing for the patient on this date
     105        billingService.autoGenerateBillingForPatientService(patientId, procedureDate);
     106
     107        return saved;
    100108    }
    101109
     
    142150
    143151        logger.info("Recorded procedure {} for patient {}", procedureId, patientId);
    144         return performedProcedureRepository.saveAndFlush(performed);
     152        PerformedProcedures saved = performedProcedureRepository.saveAndFlush(performed);
     153
     154        // Auto-generate billing for the patient on this date
     155        billingService.autoGenerateBillingForPatientService(patientId, procedureDate);
     156
     157        return saved;
    145158    }
    146159
  • backend/src/main/java/medora/service/ReferralService.java

    r0e894d2 ra95bd1b  
    11package medora.service;
    22
     3import medora.models.domain.Referrals;
    34import medora.models.domain.Doctors;
    4 import medora.models.domain.Referrals;
     5import medora.repository.ReferralRepository;
    56import medora.repository.DoctorRepository;
     7import medora.repository.PatientRepository;
    68import medora.repository.MedicalRecordRepository;
    7 import medora.repository.PatientRepository;
    8 import medora.repository.ReferralRepository;
    99import org.slf4j.Logger;
    1010import org.slf4j.LoggerFactory;
     
    1313
    1414import java.time.LocalDate;
     15import java.time.LocalTime;
    1516import java.util.List;
    1617import java.util.Optional;
     
    1920 * RefferalService handles referral operations.
    2021 * UC019 – Create Referral Record
    21  * OPTIONAL - Use only if needed
     22
    2223 */
    2324@Service
     
    3031    private final PatientRepository patientRepository;
    3132    private final MedicalRecordRepository medicalRecordRepository;
     33    private final AppointmentService appointmentService;
    3234
    3335    public ReferralService(ReferralRepository referralRepository,
    3436                           DoctorRepository doctorRepository,
    3537                           PatientRepository patientRepository,
    36                            MedicalRecordRepository medicalRecordRepository) {
     38                           MedicalRecordRepository medicalRecordRepository,
     39                           AppointmentService appointmentService) {
    3740        this.referralRepository = referralRepository;
    3841        this.doctorRepository = doctorRepository;
    3942        this.patientRepository = patientRepository;
    4043        this.medicalRecordRepository = medicalRecordRepository;
     44        this.appointmentService = appointmentService;
    4145    }
    4246
    4347    /**
    4448     * UC019 – Create Referral Record
    45      * Create a referral from one doctor to another
     49     * Create a referral from one doctor to another and automatically create an appointment
    4650     */
    4751    @Transactional
    4852    public Referrals createReferral(Long medicalRecordId, Long fromDoctorId, Long toDoctorId,
    49                                     String reason, LocalDate referralDate) {
     53                                    String reason, LocalDate referralDate, LocalDate appointmentDate, LocalTime appointmentTime) {
    5054        if (medicalRecordId == null || medicalRecordId <= 0) {
    5155            throw new IllegalArgumentException("Medical record ID must be valid");
     
    6266        if (referralDate == null) {
    6367            throw new IllegalArgumentException("Referral date is required");
     68        }
     69        if (appointmentDate == null) {
     70            throw new IllegalArgumentException("Appointment date is required");
     71        }
     72        if (appointmentTime == null) {
     73            throw new IllegalArgumentException("Appointment time is required");
    6474        }
    6575
     
    8393        referral.setReason(reason);
    8494        referral.setReferralDate(referralDate);
     95        referral.setAppointmentDate(appointmentDate);
     96        referral.setAppointmentTime(appointmentTime);
    8597
    8698        logger.info("Creating referral for medical record ID: {} from doctor ID: {} to doctor ID: {}",
    8799                medicalRecordId, fromDoctorId, toDoctorId);
    88         return referralRepository.save(referral);
     100        Referrals savedReferral = referralRepository.save(referral);
     101
     102        return savedReferral;
    89103    }
    90104
     
    143157        return referralRepository.findIncomingReferralsForDoctor(doctorId);
    144158    }
     159
     160
     161    @Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW)
     162    private void createAppointmentForReferral(Long patientId, Long doctorId, LocalDate appointmentDate, LocalTime appointmentTime) {
     163        try {
     164            appointmentService.createAppointment(patientId, doctorId, appointmentDate, appointmentTime);
     165            logger.info("Automatically created appointment for patient ID: {} with doctor ID: {} on {} at {}",
     166                    patientId, doctorId, appointmentDate, appointmentTime);
     167        } catch (RuntimeException e) {
     168            logger.warn("Failed to create appointment for referral. Error: {}", e.getMessage());
     169        }
     170    }
    145171}
Note: See TracChangeset for help on using the changeset viewer.