/* DESCRIPTION: Any functions that are used internally by the use-case */ package encounter import ( "errors" "fmt" "strings" "time" authhelper "simrs-vx/internal/lib/auth" dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" ercl "simrs-vx/internal/domain/references/clinical" erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" erg "simrs-vx/internal/domain/references/organization" eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" edo "simrs-vx/internal/domain/main-entities/device-order" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/emergency" eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" ei "simrs-vx/internal/domain/main-entities/inpatient" emo "simrs-vx/internal/domain/main-entities/material-order" emco "simrs-vx/internal/domain/main-entities/mcu-order" em "simrs-vx/internal/domain/main-entities/medication" emei "simrs-vx/internal/domain/main-entities/medication-item" emi "simrs-vx/internal/domain/main-entities/medicine-mix" emmi "simrs-vx/internal/domain/main-entities/medicine-mix-item" ep "simrs-vx/internal/domain/main-entities/prescription" epi "simrs-vx/internal/domain/main-entities/prescription-item" er "simrs-vx/internal/domain/main-entities/rehab" erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" es "simrs-vx/internal/domain/main-entities/soapi" eu "simrs-vx/internal/domain/main-entities/unit" // udo "simrs-vx/internal/use-case/main-use-case/device-order" uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" ua "simrs-vx/internal/use-case/main-use-case/ambulatory" uc "simrs-vx/internal/use-case/main-use-case/chemo" ud "simrs-vx/internal/use-case/main-use-case/doctor" ue "simrs-vx/internal/use-case/main-use-case/emergency" uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" um "simrs-vx/internal/use-case/main-use-case/medication" umei "simrs-vx/internal/use-case/main-use-case/medication-item" umi "simrs-vx/internal/use-case/main-use-case/medicine-mix" ummi "simrs-vx/internal/use-case/main-use-case/medicine-mix-item" up "simrs-vx/internal/use-case/main-use-case/prescription" upi "simrs-vx/internal/use-case/main-use-case/prescription-item" ur "simrs-vx/internal/use-case/main-use-case/rehab" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" us "simrs-vx/internal/use-case/main-use-case/soapi" ) func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { var inputSrc *e.CreateDto if inputT, ok := any(input).(*e.CreateDto); ok { inputSrc = inputT } else { inputTemp := any(input).(*e.UpdateDto) inputSrc = &inputTemp.CreateDto } data.Patient_Id = inputSrc.Patient_Id data.RegisteredAt = inputSrc.RegisteredAt data.Class_Code = inputSrc.Class_Code data.Unit_Code = inputSrc.Unit_Code data.Specialist_Code = inputSrc.Specialist_Code data.Subspecialist_Code = inputSrc.Subspecialist_Code data.VisitDate = inputSrc.VisitDate data.PaymentMethod_Code = inputSrc.PaymentMethod_Code data.InsuranceCompany_Code = inputSrc.InsuranceCompany_Code data.Member_Number = inputSrc.Member_Number data.Ref_Number = inputSrc.Ref_Number data.Trx_Number = inputSrc.Trx_Number data.Appointment_Doctor_Code = inputSrc.Appointment_Doctor_Code data.Adm_Employee_Id = inputSrc.Adm_Employee_Id data.Responsible_Doctor_Code = inputSrc.Responsible_Doctor_Code data.RefSource_Name = inputSrc.RefSource_Name data.Appointment_Id = inputSrc.Appointment_Id data.Status_Code = erc.DSCProcess data.RefType_Code = &inputSrc.RefTypeCode data.NewStatus = inputSrc.NewStatus } func setDataUpdate(src e.UpdateDto, dst *e.Encounter) { dst.Appointment_Doctor_Code = src.Appointment_Doctor_Code dst.Responsible_Doctor_Code = src.Responsible_Doctor_Code dst.Unit_Code = src.Unit_Code dst.Specialist_Code = src.Specialist_Code dst.Subspecialist_Code = src.Subspecialist_Code dst.VisitDate = src.VisitDate } func setDataDischarge(src e.DischargeDto, dst *e.Encounter) { dst.Discharge_Method_Code = src.Discharge_Method_Code dst.EarlyEducation = src.EarlyEducation dst.MedicalDischargeEducation = src.MedicalDischargeEducation dst.AdmDischargeEducation = src.AdmDischargeEducation dst.DischargeReason = src.DischargeReason dst.Status_Code = erc.DSCDone now := time.Now() dst.Discharge_Date = &now dst.FinishedAt = &now } func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { dst.Status_Code = src.StatusCode } func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) { if src.Adm_Employee_Id != nil { dst.Adm_Employee_Id = src.Adm_Employee_Id } dst.Responsible_Doctor_Code = src.Responsible_Doctor_Code dst.StartedAt = src.StartedAt } func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "checkSoapiByDocExists") var soapies []es.Soapi err := tx. Preload("Employee"). Preload("Employee.User"). Where("\"Encounter_Id\" = ?", encounter_id).Find(&soapies).Error if err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get soapi failed", Raw: err, } return pl.SetLogError(event, nil) } if len(soapies) == 0 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-notFound", Detail: "no soapi found for encounter", Raw: errors.New("soapi not found"), } return pl.SetLogError(event, nil) } for _, s := range soapies { if s.Employee != nil && *s.Employee.Position_Code == erg.EPCDoc { return nil } } event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "no soapi written by a doctor found", Raw: errors.New("all soapi employees are not doctors"), } return pl.SetLogError(event, nil) } func createMedication(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "createMedication") prescription, err := up.ReadDetailData(ep.ReadDetailDto{Encounter_Id: &encounter_id}, event, tx) if err != nil { return err } prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{ FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id}, Includes: "medicineMix,medicineMix-MixItems"}, event, tx) if err != nil { return err } if len(prescriptionItem) == 0 { return nil } medicationCreate := em.CreateDto{ Encounter_Id: &encounter_id, IssuedAt: pu.GetTimeNow(), Status_Code: erc.DSCNew, } medication, err := um.CreateData(medicationCreate, event, tx) if err != nil { return err } for _, prescriptionItem := range prescriptionItem { if prescriptionItem.IsMix { medMix_id, err := createMedicineMixAndItem(*prescriptionItem.MedicineMix, event, tx) if err != nil { return err } prescriptionItem.MedicineMix_Id = medMix_id } err := createMedicationItem(medication.Id, prescriptionItem, event, tx) if err != nil { return err } } return nil } func createMedicineMixAndItem(input emi.MedicineMix, event *pl.Event, tx *gorm.DB) (*uint, error) { pl.SetLogInfo(event, nil, "started", "createMedicineMix") medicineMixCreate := emi.CreateDto{ Name: input.Name, Uom_Code: input.Uom_Code, } medicineMix, err := umi.CreateData(medicineMixCreate, event, tx) if err != nil { return nil, err } // recreate medicineMixItem with new medicineMix_id to keep medMixItem remain the same for prescriptionItem that is created for _, medicineMixItem := range input.MixItems { medicineMixItemCreate := emmi.CreateDto{ MedicineMix_Id: &medicineMix.Id, Medicine_Code: medicineMixItem.Medicine_Code, Dose: medicineMixItem.Dose, } _, err := ummi.CreateData(medicineMixItemCreate, event, tx) if err != nil { return nil, err } } return &medicineMix.Id, nil } func createMedicationItem(medication_id uint, input epi.PrescriptionItem, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "createMedicationItem") medicationItemCreate := emei.CreateDto{ Medication_Id: &medication_id, IsMix: input.IsMix, Medicine_Code: input.Medicine_Code, MedicineMix_Id: input.MedicineMix_Id, Usage: input.Usage, Interval: input.Interval, IntervalUnit_Code: input.IntervalUnit_Code, Quantity: input.Quantity, } _, err := umei.CreateData(medicationItemCreate, event, tx) if err != nil { return err } return nil } func checkNewOrdersExist(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "CheckNewOrdersExist") var errs []string if err := getDeviceOrders(encounter_id, event, tx); err != nil { errs = append(errs, err.Error()) } if err := getMaterialOrders(encounter_id, event, tx); err != nil { errs = append(errs, err.Error()) } if err := getMcuOrders(encounter_id, event, tx); err != nil { errs = append(errs, err.Error()) } if len(errs) > 0 { return fmt.Errorf("encounter has open orders: %s", strings.Join(errs, "; ")) } return nil } func getDeviceOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "getDeviceOrders") var orders []edo.DeviceOrder err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error if err != nil { if err == gorm.ErrRecordNotFound { return nil } event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get device order failed", Raw: err, } return pl.SetLogError(event, nil) } if len(orders) > 0 { return fmt.Errorf("encounter has %d device orders", len(orders)) } return nil } func getMaterialOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "getMaterialOrders") var orders []emo.MaterialOrder err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error if err != nil { if err == gorm.ErrRecordNotFound { return nil } event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get material order failed", Raw: err, } return pl.SetLogError(event, nil) } if len(orders) > 0 { return fmt.Errorf("encounter has %d material orders", len(orders)) } return nil } func getMcuOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error { pl.SetLogInfo(event, nil, "started", "getMcuOrders") var orders []emco.McuOrder err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error if err != nil { if err == gorm.ErrRecordNotFound { return nil } event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get mcu order failed", Raw: err, } return pl.SetLogError(event, nil) } if len(orders) > 0 { return fmt.Errorf("encounter has %d mcu orders", len(orders)) } return nil } func upsertResponsibleDoctorHist(input erdh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "DBCreate") var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } var latest erdh.ResponsibleDoctorHist err := tx. Where("\"Encounter_Id\" = ?", input.Encounter_Id). Order("\"CreatedAt\" DESC"). Limit(1). First(&latest).Error switch { case errors.Is(err, gorm.ErrRecordNotFound): // Insert if _, err = urdh.CreateData(input, event, tx); err != nil { return err } case err != nil: event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "read-fail", Detail: "Failed to read responsible doctor history", Raw: err, } return pl.SetLogError(event, input) default: // Update if err := tx.Model(&latest).Updates(map[string]interface{}{ "Doctor_Code": input.Doctor_Code, "StartedAt": input.StartedAt, "UpdatedAt": time.Now(), }).Error; err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "update-fail", Detail: "Failed to update responsible doctor history", Raw: err, } return pl.SetLogError(event, input) } } pl.SetLogInfo(event, input, "complete") return nil } func upsertAdmEmployeeHist(input eaeh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "DBCreate") var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } var latest eaeh.AdmEmployeeHist err := tx. Where("\"Encounter_Id\" = ?", input.Encounter_Id). Order("\"CreatedAt\" DESC"). Limit(1). First(&latest).Error switch { case errors.Is(err, gorm.ErrRecordNotFound): // Insert if _, err = uaeh.CreateData(input, event, tx); err != nil { return err } case err != nil: event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "read-fail", Detail: "Failed to read responsible doctor history", Raw: err, } return pl.SetLogError(event, input) default: // Update if err := tx.Model(&latest).Updates(map[string]interface{}{ "Employee_Id": input.Employee_Id, "StartedAt": input.StartedAt, "UpdatedAt": time.Now(), }).Error; err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "update-fail", Detail: "Failed to update responsible doctor history", Raw: err, } return pl.SetLogError(event, input) } } pl.SetLogInfo(event, input, "complete") return nil } func updateLatestResponsibleDoctorHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, "started", "DBUpdate") var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } subQuery := tx. Select("\"Id\""). Model(&erdh.ResponsibleDoctorHist{}). Where("\"Encounter_Id\" = ?", input.Id). Order("\"CreatedAt\" DESC"). Limit(1) result := tx. Model(&erdh.ResponsibleDoctorHist{}). Where("\"Id\" = (?)", subQuery). Update("\"FinishedAt\"", input.FinishedAt) if result.Error != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "Database update failed", Raw: result.Error, } return pl.SetLogError(event, input) } if result.RowsAffected == 0 { pl.SetLogInfo(event, input, "no previous data found to update") return nil } pl.SetLogInfo(event, nil, "complete") return nil } func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, "started", "DBUpdate") var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } subQuery := tx. Select("\"Id\""). Model(&eaeh.AdmEmployeeHist{}). Where("\"Encounter_Id\" = ?", input.Id). Order("\"CreatedAt\" DESC"). Limit(1) result := tx. Model(&eaeh.AdmEmployeeHist{}). Where("\"Id\" = (?)", subQuery). Update("\"FinishedAt\"", input.FinishedAt) if result.Error != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "Database update failed", Raw: result.Error, } return pl.SetLogError(event, input) } if result.RowsAffected == 0 { pl.SetLogInfo(event, input, "no previous data found to update") return nil } pl.SetLogInfo(event, nil, "complete") return nil } func getSoapiByResponsibleDoctor(enc e.Encounter, event *pl.Event) (data []es.Soapi, err error) { pl.SetLogInfo(event, enc, "started", "DBReadList") if enc.Responsible_Doctor == nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "no responsible-doctor found", Detail: "Encounter does not have responsible-doctor", } } err = dg.I. Model(&es.Soapi{}). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Where("\"Encounter_Id\" = ?", enc.Id). Where("\"Employee\".\"Position_Code\" = ?", erg.EPCDoc). Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc}). Where("\"Soapi\".\"Employee_Id\" = ?", *enc.Responsible_Doctor.Employee_Id). Find(&data).Error if err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Raw: err, Code: "read-fail", Detail: "Database read failed", } return nil, pl.SetLogError(event, enc) } pl.SetLogInfo(event, nil, "complete") return } func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.CreateDto, err error) { data, err := getSoapiByResponsibleDoctor(enc, event) if err != nil { return nil, err } for _, s := range data { // set data soapi for copy dataSoapi = append(dataSoapi, es.CreateDto{ Employee_Id: s.Employee_Id, Time: s.Time, TypeCode: s.TypeCode, Value: s.Value, }) } if len(dataSoapi) < 2 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "missing-soapi", Detail: fmt.Sprintf("Missing required Soapi types"), } return nil, pl.SetLogError(event, enc) } return } func getSoapiByTypeCode(encounter *e.Encounter, event *pl.Event, mode string) (err error) { pl.SetLogInfo(event, encounter, "started", "DBReadList") var ( dataSoapi []es.Soapi amb = encounter.Ambulatory rehab = encounter.Rehab ) // Set Query for get data Soapi tx := dg.I. Model(&es.Soapi{}). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Where("\"Encounter_Id\" = ?", encounter.Id). Where("\"Employee\".\"Position_Code\" = ?", erg.EPCDoc) // Set Case switch { case amb.Class_Code == ere.ACCReg: tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEEarlyMedic) case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCAdm: tx = tx.Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab}) case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCSeries: tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEarlyRehab) } if err = tx.Find(&dataSoapi).Error; err != nil { return setDBError(event, err, encounter) } pl.SetLogInfo(event, nil, "complete") return validateExistedSoapi(dataSoapi, encounter, event, mode) } func validateExistedSoapi(dataSoapi []es.Soapi, dataEncounter *e.Encounter, event *pl.Event, mode string) error { var ( amb = dataEncounter.Ambulatory rehab = dataEncounter.Rehab ) typeExist := make(map[ercl.SoapiTypeCode]bool) for _, s := range dataSoapi { typeExist[s.TypeCode] = true } required := []ercl.SoapiTypeCode{} switch { case amb.Class_Code == ere.ACCReg: required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic} case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCAdm: required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab} case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCSeries: required = []ercl.SoapiTypeCode{ercl.STCEarlyRehab} } var missing, existing []string for _, code := range required { if typeExist[code] { existing = append(existing, string(code)) } else { missing = append(missing, string(code)) } } switch mode { case "check-in": if len(existing) > 0 { return setSoapiError(event, fmt.Sprintf("Soapi type(s) %s exist, can't check-in", strings.Join(existing, ", "))) } case "check-out": if len(missing) > 0 { return setSoapiError(event, fmt.Sprintf("Soapi type(s) %s not found, can't check-out", strings.Join(missing, ", "))) } } return nil } func identifyPatientStatus(input e.CreateDto) (isNewPatient bool, err error) { dataPatient, err := ReadList(e.ReadListDto{ FilterDto: e.FilterDto{Patient_Id: input.Patient_Id}, AuthInfo: authhelper.AuthInfo{User_Id: input.User_Id}}) if err != nil { return } if list, ok := dataPatient.Data.([]e.ResponseDto); ok { if len(list) < 1 { isNewPatient = true } } return } func determineVisitMode(recentRehabData *er.Rehab, input e.CreateDto, event *pl.Event) (ere.VisitModeCode, *e.Encounter, error) { var ( visitModeCode ere.VisitModeCode recentAdmEncounterData e.Encounter isQuotaValid bool err error ) switch *recentRehabData.Status_Code { case erc.DSCProcess: visitModeCode = ere.VMCSeries // verify whether the allocated visit count has not exceeded the limit recentAdmEncounterData, isQuotaValid, err = verifyAllocatedVisitCount(input, event) if err != nil { return "", nil, err } if !isQuotaValid { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "visit-limit-exceeded", Detail: "Encounter has exceeded the allowed number of visits", Raw: errors.New("visit count exceeds allowed limit"), } return "", nil, pl.SetLogError(event, input) } if recentRehabData.ExpiredAt != nil && recentRehabData.ExpiredAt.Before(*pu.GetTimeNow()) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "visit-limit-exceeded", Detail: "Encounter period has expired", Raw: errors.New("encounter expired"), } return "", nil, pl.SetLogError(event, input) } case erc.DSCDone: visitModeCode = ere.VMCAdm default: event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "invalid-status", Detail: fmt.Sprintf("Unknown rehab status: %v", *recentRehabData.Status_Code), Raw: errors.New("unsupported rehab status"), } return "", nil, pl.SetLogError(event, input) } return visitModeCode, &recentAdmEncounterData, nil } func insertdataClassCode(input e.CreateDto, soapiData []es.CreateDto, event *pl.Event, tx *gorm.DB) (err error) { switch input.Class_Code { case ere.ECAmbulatory: subCode := ere.AmbulatoryClassCode(*input.SubClass_Code) ambCreate := ea.CreateDto{ Encounter_Id: &input.Id, Class_Code: subCode, } // create data Ambulatory _, err = ua.CreateData(ambCreate, event, tx) if err != nil { return err } // insert chemo/rehab err = insertDataSubClassAmbulatory(input, soapiData, event, tx) if err != nil { return err } case ere.ECEmergency: subCode := ere.EmergencyClassCode(*input.SubClass_Code) emerCreate := ee.CreateDto{ Encounter_Id: &input.Id, Class_Code: subCode, } // create data emergency _, err = ue.CreateData(emerCreate, event, tx) if err != nil { return err } case ere.ECInpatient: subCode := ere.InpatientClassCode(*input.SubClass_Code) inpCreate := ei.CreateDto{ Encounter_Id: &input.Id, Class_Code: subCode, Infra_Code: input.Infra_Code, } // create data inpatient _, err = ui.CreateData(inpCreate, event, tx) if err != nil { return err } default: return errors.New("invalid encounter class code") } return } func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, event *pl.Event, tx *gorm.DB) (err error) { subCode := ere.AmbulatoryClassCode(*input.SubClass_Code) switch { case subCode == ere.ACCChemo: chemoCreate := ec.CreateDto{ Encounter_Id: &input.Id, Status_Code: erc.DVCNew, SrcUnit_Code: input.Unit_Code, } // create data chemo _, err = uc.CreateData(chemoCreate, event, tx) if err != nil { return err } case subCode == ere.ACCRehab: rehabData := er.CreateDto{ Encounter_Id: &input.Id, VisitMode_Code: input.VisitMode_Code, Status_Code: erc.DSCProcess, } // if visitMode_code is series, then bulk insert soapi if input.VisitMode_Code == ere.VMCSeries { rehabData.Parent_Encounter_Id = &input.RecentEncounterAdm.Id // Insert Soapi if err = us.CreateBulkData(soapiData, input.Id, event, tx); err != nil { return err } } // create rehab if _, err = ur.CreateData(rehabData, event, tx); err != nil { return err } } return } func verifyRehabLimit(data *e.Encounter, event *pl.Event, tx *gorm.DB) error { // get data encounter adm encounterAdmData, _, err := verifyAllocatedVisitCount(e.CreateDto{Patient_Id: data.Patient_Id}, event) if err != nil { return err } // Check if the visit count has reached the allowed limit // Mark latest rehab status as 'done' if exceeded. if len(*encounterAdmData.RehabChildren) >= *encounterAdmData.Rehab.AllocatedVisitCount { err = updateRehabStatus(er.UpdateDto{ Id: uint16(data.Rehab.Id), CreateDto: er.CreateDto{Status_Code: erc.DSCDone}}, event, tx) if err != nil { return err } } return nil } func updateRehabStatus(input er.UpdateDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, "started", "DBUpdate") var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } result := tx. Model(&er.Rehab{}). Where("\"Id\" = (?)", input.Id). Update("\"Status_Code\"", input.Status_Code) if result.Error != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "Database update failed", Raw: result.Error, } return pl.SetLogError(event, input) } pl.SetLogInfo(event, nil, "complete") return nil } func validateForeignKey(input e.CheckinDto) error { // validate employee_Id if input.Adm_Employee_Id != nil { if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Adm_Employee_Id)}); err != nil { return err } } // validate doctor_Code if input.Responsible_Doctor_Code != nil { if _, err := ud.ReadDetail(ed.ReadDetailDto{Code: input.Responsible_Doctor_Code}); err != nil { return err } } return nil } func setSoapiError(event *pl.Event, detail string) error { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "invalid-soapi-state", Detail: detail, } return pl.SetLogError(event, detail) } func setDBError(event *pl.Event, err error, ctx any) error { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Raw: err, Code: "read-fail", Detail: "Database read failed", } return pl.SetLogError(event, ctx) } func getUnits(unitIds []string, event *pl.Event) ([]eu.Unit, error) { pl.SetLogInfo(event, nil, "started", "getUnits") var units []eu.Unit err := dg.I.Where("\"Code\" IN ?", unitIds).Find(&units).Error if err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get units", Raw: err, } return nil, pl.SetLogError(event, nil) } return units, nil } func getDoctors(doctorIds []string, event *pl.Event) ([]ed.Doctor, error) { pl.SetLogInfo(event, nil, "started", "getDoctors") var doctors []ed.Doctor err := dg.I.Where("\"Code\" IN ?", doctorIds).Find(&doctors).Error if err != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-get-fail", Detail: "get doctors", Raw: err, } return nil, pl.SetLogError(event, nil) } return doctors, nil } func validateUnitCodes(unitCodes map[string]struct{}, event *pl.Event) error { if len(unitCodes) > 0 { var codes []string for code := range unitCodes { codes = append(codes, code) } units, err := getUnits(codes, event) if err != nil { return fmt.Errorf("failed to fetch units: %w", err) } if len(units) != len(codes) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-validation-fail", Detail: "unit_code not found", } return pl.SetLogError(event, nil) } } return nil } func validateDoctorCodes(doctorCodes map[string]struct{}, event *pl.Event) error { if len(doctorCodes) > 0 { var codes []string for code := range doctorCodes { codes = append(codes, code) } doctors, err := getDoctors(codes, event) if err != nil { return fmt.Errorf("failed to fetch doctors: %w", err) } if len(doctors) != len(codes) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-validation-fail", Detail: "doctor_code not found", } return pl.SetLogError(event, nil) } } return nil }