/* DESCRIPTION: Any functions that are used internally by the use-case */ package encounter import ( "errors" "fmt" "strings" "time" 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" edo "simrs-vx/internal/domain/main-entities/device-order" ed "simrs-vx/internal/domain/main-entities/doctor" e "simrs-vx/internal/domain/main-entities/encounter" 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" eu "simrs-vx/internal/domain/main-entities/unit" // udo "simrs-vx/internal/use-case/main-use-case/device-order" es "simrs-vx/internal/domain/main-entities/soapi" uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" uir "simrs-vx/internal/use-case/main-use-case/internal-reference" 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" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" ) 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_Id = inputSrc.Unit_Id data.Specialist_Id = inputSrc.Specialist_Id data.Subspecialist_Id = inputSrc.Subspecialist_Id data.VisitDate = inputSrc.VisitDate data.PaymentMethod_Code = inputSrc.PaymentMethod_Code data.InsuranceCompany_Id = inputSrc.InsuranceCompany_Id data.Member_Number = inputSrc.Member_Number data.Ref_Number = inputSrc.Ref_Number data.Trx_Number = inputSrc.Trx_Number data.Appointment_Doctor_Id = inputSrc.Appointment_Doctor_Id data.Adm_Employee_Id = inputSrc.Adm_Employee_Id data.Responsible_Doctor_Id = inputSrc.Responsible_Doctor_Id 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_Id = src.Appointment_Doctor_Id dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id dst.Unit_Id = src.Unit_Id dst.Specialist_Id = src.Specialist_Id dst.Subspecialist_Id = src.Subspecialist_Id 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 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_Id: medicineMixItem.Medicine_Id, 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_Id: input.Medicine_Id, 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 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_Id = src.Responsible_Doctor_Id dst.StartedAt = src.StartedAt } func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error { unitIDs := make(map[uint16]struct{}) doctorIDs := make(map[uint]struct{}) for _, ref := range *input.InternalReferences { if ref.Unit_Id != nil { unitIDs[*ref.Unit_Id] = struct{}{} } if ref.Doctor_Id != nil { doctorIDs[*ref.Doctor_Id] = struct{}{} } } // validate unitIds if len(unitIDs) > 0 { var ids []uint16 for id := range unitIDs { ids = append(ids, id) } units, err := getUnits(ids, event, tx) if err != nil { return fmt.Errorf("failed to fetch units: %w", err) } if len(units) != len(ids) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-validation-fail", Detail: "unit_id not found", } return pl.SetLogError(event, nil) } } // validate doctorIds if len(doctorIDs) > 0 { var ids []uint for id := range doctorIDs { ids = append(ids, id) } doctors, err := getDoctors(ids, event, tx) if err != nil { return fmt.Errorf("failed to fetch doctors: %w", err) } if len(doctors) != len(ids) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-validation-fail", Detail: "doctor_id not found", } return pl.SetLogError(event, nil) } } if err := uir.CreateBulkData(*input.InternalReferences, input.Id, event, tx); err != nil { return err } return nil } func getUnits(unitIds []uint16, event *pl.Event, tx *gorm.DB) ([]eu.Unit, error) { pl.SetLogInfo(event, nil, "started", "getUnits") var units []eu.Unit err := tx.Where("\"Id\" 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 []uint, event *pl.Event, tx *gorm.DB) ([]ed.Doctor, error) { pl.SetLogInfo(event, nil, "started", "getDoctors") var doctors []ed.Doctor err := tx.Where("\"Id\" 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 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_Id": input.Doctor_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 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.StartedAt) 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.StartedAt) 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 getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.CreateDto, err error) { var data []es.Soapi 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, } if errors.Is(err, gorm.ErrRecordNotFound) { event.ErrInfo.Code = "data-not-found" event.ErrInfo.Detail = "Data not found" return nil, pl.SetLogError(event, enc) } event.ErrInfo.Code = "read-fail" event.ErrInfo.Detail = "Database read failed" return nil, pl.SetLogError(event, enc) } pl.SetLogInfo(event, nil, "complete") 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(encounterId uint, dataAmbulatory ea.Ambulatory, event *pl.Event, mode string) (err error) { pl.SetLogInfo(event, encounterId, "started", "DBReadList") var ( dataSoapi []es.Soapi ) // Set Query for get data Soapi tx := dg.I. Model(&es.Soapi{}). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Where("\"Encounter_Id\" = ?", encounterId). Where("\"Employee\".\"Position_Code\" = ?", erg.EPCDoc) // Set Case switch { case dataAmbulatory.Class_Code == ere.ACCReg: tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEEarlyMedic) case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: tx = tx.Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab}) case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCSeries: tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEarlyRehab) } if err = tx.Find(&dataSoapi).Error; err != nil { return setDBError(event, err, encounterId) } pl.SetLogInfo(event, nil, "complete") return validateExistedSoapi(dataSoapi, &dataAmbulatory, event, mode) } func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, event *pl.Event, mode string) error { typeExist := make(map[ercl.SoapiTypeCode]bool) for _, s := range dataSoapi { typeExist[s.TypeCode] = true } required := []ercl.SoapiTypeCode{} switch { case dataAmbulatory.Class_Code == ere.ACCReg: required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic} case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab} case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.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 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 updateRehabDoctor(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("\"Encounter_Id\" = (?)", input.Encounter_Id). Update("\"Doctor_Id\"", input.Doctor_Id) 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 }