package encounter import ( "errors" "fmt" "simrs-vx/internal/lib/auth" "strconv" "time" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" "gorm.io/gorm" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" erg "simrs-vx/internal/domain/references/organization" ev "simrs-vx/internal/domain/bpjs-entities/vclaim-reference" edc "simrs-vx/internal/domain/main-entities/death-cause" e "simrs-vx/internal/domain/main-entities/encounter" eir "simrs-vx/internal/domain/main-entities/internal-reference" ep "simrs-vx/internal/domain/main-entities/patient" es "simrs-vx/internal/domain/main-entities/soapi" esync "simrs-vx/internal/domain/sync-entities/log" uv "simrs-vx/internal/use-case/bpjs-use-case/vclaim-reference" udc "simrs-vx/internal/use-case/main-use-case/death-cause" uir "simrs-vx/internal/use-case/main-use-case/internal-reference" up "simrs-vx/internal/use-case/main-use-case/patient" us "simrs-vx/internal/use-case/main-use-case/soapi" ) const source = "encounter" var now = time.Now() func Create(input e.CreateDto) (*d.Data, error) { var ( data e.Encounter recentSoapiDataforCopy []es.CreateDto err error ) event := pl.Event{ Feature: "Create", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "create") roleAllowed := []string{string(erg.EPCReg)} err = validateAuth(input.AuthInfo, roleAllowed, "create-encounter", &event) if err != nil { return nil, err } // validate rehab by bpjs if input.RefTypeCode == ere.RTCBpjs && input.Class_Code == ere.ECAmbulatory && ere.AmbulatoryClassCode(*input.SubClass_Code) == ere.ACCRehab { // get latest rehab data recentRehabData, err := getLatestRehabData(input, &event) if err != nil { return nil, err } // If recentRehabData is nil, then visitMode_Code = "adm" if recentRehabData != nil { // If recentRehabData is not nil, determine the visitMode_Code: // If the mode is "series", verify whether the visit count still remains // and whether the series has not expired. // If visitMode is "series", then get encounterAdm input.VisitMode_Code, input.RecentEncounterAdm, err = determineVisitMode(recentRehabData, input, &event) if err != nil { return nil, err } } else { input.VisitMode_Code = ere.VMCAdm } // When visitMode_Code is "series", load the associated SOAPI record to copy its values. if input.VisitMode_Code == ere.VMCSeries { // get data soapi recentSoapiDataforCopy, err = getSoapiEncounterAdm(*input.RecentEncounterAdm, &event) if err != nil { return nil, err } } } // check if patient is new in the hospital input.NewStatus, err = identifyPatientStatus(input) input.Adm_Employee_Id = input.AuthInfo.Employee_Id mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { // create encounter if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData input.Id = data.Id } // insert ambulatory/emergency/inpatient err = insertdataClassCode(input, recentSoapiDataforCopy, &event, tx) if err != nil { return err } // insert vclaimReference if vr := input.VclaimReference; vr != nil { t, _ := time.Parse("2006-01-02", vr.TglRujukan) _, err = uv.CreateData(ev.CreateDto{ Encounter_Id: &data.Id, Date: &t, SrcCode: input.Ref_Number, SrcName: input.RefSource_Name, Number: &vr.NoSep}, &event, tx) } dataEncounter, err := ReadDetailData(e.ReadDetailDto{ Id: data.Id, Includes: "Adm_Employee.User,Adm_Employee.Person," + "Patient.Person.Relatives," + "Patient.Person.VclaimMember,VclaimReference," + "Patient.Person.Contacts,Patient.Person.Addresses"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCreateMiddleware(createPreMw, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.II{ "source": source, "structure": "single-data", "status": "created", }, Data: data.ToResponse(), }, nil } func ReadList(input e.ReadListDto) (*d.Data, error) { var dataList []e.Encounter var metaList *e.MetaDto var err error event := pl.Event{ Feature: "ReadList", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { return err } return nil }) if err != nil { return nil, err } return &d.Data{ Meta: d.IS{ "source": source, "structure": "list-data", "status": "fetched", "page_number": strconv.Itoa(metaList.PageNumber), "page_size": strconv.Itoa(metaList.PageSize), "record_totalCount": strconv.Itoa(metaList.Count), "record_currentCount": strconv.Itoa(len(dataList)), }, Data: e.ToResponseList(dataList), }, nil } func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { var data *e.Encounter var err error event := pl.Event{ Feature: "ReadDetail", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { if data, err = ReadDetailData(input, &event, tx); err != nil { return err } return nil }) if err != nil { return nil, err } return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "fetched", }, Data: data.ToResponse(), }, nil } func Update(input e.UpdateDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id} var data *e.Encounter var err error event := pl.Event{ Feature: "Update", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "update") roleAllowed := []string{string(erg.EPCReg)} err = validateAuth(input.AuthInfo, roleAllowed, "update-encounter", &event) if err != nil { return nil, err } input.Adm_Employee_Id = input.AuthInfo.Employee_Id mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } if err := UpdateData(input, data, &event, tx); err != nil { return err } dataEncounter, err := ReadDetailData(e.ReadDetailDto{ Id: data.Id, Includes: "Adm_Employee.User,Patient.Person.Relatives,Patient.Person.VclaimMember"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run post-middleware if err := mwRunner.RunUpdateMiddleware(updatePreMw, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "updated", }, Data: data.ToResponse(), }, nil } func Delete(input e.DeleteDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id} var data *e.Encounter var err error event := pl.Event{ Feature: "Delete", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "delete") roleAllowed := []string{string(erg.EPCReg)} err = validateAuth(input.AuthInfo, roleAllowed, "delete-encounter", &event) if err != nil { return nil, err } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } // Prevent delete if soapi exist dataSoapi, _, err := us.ReadListData(es.ReadListDto{ FilterDto: es.FilterDto{Encounter_Id: &data.Id}}, &event, tx) if err != nil { return err } if len(dataSoapi) > 0 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "cancel-not-allowed", Detail: "encounter can't be deleted because SOAPI already exists", Raw: errors.New("encounter has SOAPI records"), } return pl.SetLogError(&event, input) } if err := DeleteData(data, &event, tx); err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "deleted", }, Data: data.ToResponse(), }, nil } func CheckOut(input e.DischargeDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id, Includes: "Ambulatory,Rehab,InternalReferences"} var data *e.Encounter var err error event := pl.Event{ Feature: "CheckOut", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "checkOut") roleAllowed := []string{string(erg.EPCNur)} err = validateAuth(input.AuthInfo, roleAllowed, "checkOut-encounter", &event) if err != nil { return nil, err } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) { // Validate SOAPI completeness: abort checkout if any required SOAPI is missing. if err = getSoapiByTypeCode(data, &event, "check-out"); err != nil { return err } if data.Ambulatory.Class_Code == ere.ACCRehab { // verify and update rehabData to DONE if visit count has reached the allowed limit if err = verifyRehabLimit(data, &event, tx); err != nil { return err } } } else { // TODO: chemo TBC (to be confirm) if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { return err } } // update encounter if err := updateDischargeData(input, data, &event, tx); err != nil { return err } if err := checkNewOrdersExist(data.Id, &event, tx); err != nil { return err } if err := createMedication(data.Id, &event, tx); err != nil { if !pu.IsDataNotFoundError(err) { return err } } // insert data death-cause if *input.Discharge_Method_Code == ere.DMCDeath { if _, err = udc.CreateData(edc.CreateDto{Encounter_Id: &input.Id, Value: input.DeathCause}, &event, tx); err != nil { return err } } // perform additional validation when discharge_method_code is "consul-poly" or "consul-executive" if *data.Discharge_Method_Code == ere.DMCConsulPoly || *data.Discharge_Method_Code == ere.DMCConsulExecutive { for _, v := range *data.InternalReferences { if *v.Status_Code == erc.DACNew { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "checkout is not allowed because the internal-reference status is 'new'", Raw: errors.New("internal-referral status not done yet"), } return pl.SetLogError(&event, input) } } } // read the newest data encounter for sync dataEncounter, err := ReadDetailData(e.ReadDetailDto{Id: data.Id, Includes: "DeathCause"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCheckoutMiddleware(checkoutEncounter, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "checkOut", }, Data: data.ToResponse(), }, nil } func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id} var data *e.Encounter var err error event := pl.Event{ Feature: "Update Status Code", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "update") roleAllowed := []string{string(erg.EPCReg), string(erg.EPCNur), string(erg.EPCDoc)} err = validateAuth(input.AuthInfo, roleAllowed, "update-status-encounter", &event) if err != nil { return nil, err } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } if input.StatusCode == erc.DSCCancel { // TODO: Prevent cancellation if the billing has been verified // Prevent cancellation if soapi exist dataSoapi, _, err := us.ReadListData(es.ReadListDto{ FilterDto: es.FilterDto{Encounter_Id: &data.Id}}, &event, tx) if err != nil { return err } if len(dataSoapi) > 0 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "cancel-not-allowed", Detail: "encounter can't be cancelled because SOAPI already exists", Raw: errors.New("encounter has SOAPI records"), } return pl.SetLogError(&event, input) } } if err := UpdateStatusData(input, &event, tx); err != nil { return err } // get data encounter if data, err = ReadDetailData(e.ReadDetailDto{ Id: input.Id, Includes: "Adm_Employee.User"}, &event, tx); err != nil { return err } if input.StatusCode == erc.DSCProcess || input.StatusCode == erc.DSCCancel { mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunUpdateStatusMiddleware(updatestatusEncounter, data); err != nil { return err } } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "updated", }, Data: data.ToResponse(), }, nil } func CheckIn(input e.CheckinDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id, Includes: "Rehab,Ambulatory"} var data *e.Encounter var err error event := pl.Event{ Feature: "CheckIn", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "checkIn") roleAllowed := []string{string(erg.EPCNur), string(erg.EPCDoc)} err = validateAuth(input.AuthInfo, roleAllowed, "checkIn-encounter", &event) if err != nil { return nil, err } input.Responsible_Nurse_Code = input.AuthInfo.Nurse_Code // validate foreign key if err := validateForeignKey(input, &event); err != nil { return nil, err } // set startedAt if input.StartedAt == nil { input.StartedAt = &now } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } // prevent repeated check-in. If SOAPI already exists, block the check-in process. if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) { err = getSoapiByTypeCode(data, &event, "check-in") if err != nil { return err } } // update encounter data if err := updateCheckInData(input, data, &event, tx); err != nil { return err } // read the newest data encounter for sync dataEncounter, err := ReadDetailData(e.ReadDetailDto{Id: data.Id, Includes: "Responsible_Nurse.Employee.User"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCheckinMiddleware(checkinEncounterMw, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "checkIn", }, Data: data.ToResponse(), }, nil } func RequestSwitchSpecialist(input e.SwitchSpecialistDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id, Includes: "Responsible_Nurse.Employee.User,Responsible_Doctor.Employee"} var data *e.Encounter var err error event := pl.Event{ Feature: "RequestSwitchSpecialist", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "checkOut") unitCodes := make(map[string]struct{}) doctorCodes := make(map[string]struct{}) for _, ref := range *input.InternalReferences { if ref.Specialist_Code != nil { unitCodes[*ref.Specialist_Code] = struct{}{} } if ref.Doctor_Code != nil { doctorCodes[*ref.Doctor_Code] = struct{}{} } } // validate unit if err = validateSpecialistCodes(unitCodes, &event); err != nil { return nil, err } // validate doctor if err = validateDoctorCodes(doctorCodes, &event); err != nil { return nil, err } roleAllowed := []string{string(erg.EPCNur)} err = validateAuth(input.AuthInfo, roleAllowed, "request-switch-poly", &event) if err != nil { return nil, err } input.Src_Nurse_Code = input.AuthInfo.Nurse_Code mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } input.Src_Doctor_Code = data.Responsible_Doctor_Code if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } // Poly-Switch-Requests can only be processed if the previous responsible doctor has completed their SOAPI. dataSoapi, err := getSoapiByResponsibleDoctor(*data, &event) if err != nil { return err } if len(dataSoapi) < 1 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "missing-soapi", Detail: "Missing soapi from latest responsible doctor", } return pl.SetLogError(&event, input) } // update encounter discharge_method_code if err = UpdateDischargeMethod(input, &event, tx); err != nil { return err } // bulk internal references if err := uir.CreateBulkData(&input, &event, tx); err != nil { return err } // read the newest data encounter for sync dataEncounter, err := ReadDetailData(e.ReadDetailDto{Id: data.Id, Includes: "InternalReferences,Responsible_Nurse.Employee.User"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunRequestSwitchSpecialistMiddleware(requestSwitchEncounter, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "requestSwitchSpecialist", }, Data: data.ToResponse(), }, nil } func ApproveSwitchSpecialist(input e.ApproveCancelSpecialistDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id, Includes: "Responsible_Doctor.Employee"} var ( data *e.Encounter err error ) event := pl.Event{ Feature: "ApproveSwitchSpecialist", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "approveSwitchSpecialist") roleAllowed := []string{string(erg.EPCNur)} err = validateAuth(input.AuthInfo, roleAllowed, "request-switch-poly", &event) if err != nil { return nil, err } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } // get internalReference based on internalReferences_Id irData, err := uir.ReadDetailData(eir.ReadDetailDto{ Id: input.InternalReferences_Id, Includes: "Doctor"}, &event, tx) if err != nil { return err } if *irData.Status_Code != erc.DACNew { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "internal references is approve", Raw: errors.New("internal references is approve"), } return pl.SetLogError(&event, input) } // Approve-Switch-Requests can only be processed if the previous responsible doctor has completed their SOAPI. dataSoapi, err := getSoapiByResponsibleDoctor(*data, &event) if err != nil { return err } if len(dataSoapi) < 1 { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "missing-soapi", Detail: "Missing soapi from latest responsible doctor", } return pl.SetLogError(&event, input) } // Set doctor_code; nil indicates no change. if input.Dst_Doctor_Code == nil || *input.Dst_Doctor_Code == "" { input.Dst_Doctor_Code = irData.Doctor_Code } // update internal reference if err = uir.UpdateData(eir.UpdateDto{ Id: input.InternalReferences_Id, CreateDto: eir.CreateDto{ Status_Code: erc.DACApproved, Doctor_Code: input.Dst_Doctor_Code, Nurse_Code: input.Nurse_Code}}, irData, &event, tx); err != nil { return err } // update encounter if err = updateEncounterApproveSwitchSpecialist(input, &event, tx); err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunApproveSwitchSpecialistMiddleware(approveSwitchEncounter, &input); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "updated", }, Data: data.ToResponse(), }, nil } func CancelSwitchSpecialist(input e.ApproveCancelSpecialistDto) (*d.Data, error) { rdDto := e.ReadDetailDto{Id: input.Id} var ( data *e.Encounter err error ) event := pl.Event{ Feature: "CancelSwitchSpecialist", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "cancelSwitchSpecialist") roleAllowed := []string{string(erg.EPCNur)} err = validateAuth(input.AuthInfo, roleAllowed, "request-switch-poly", &event) if err != nil { return nil, err } mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } if data.IsDone() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "encounter is done", Raw: errors.New("encounter is done"), } return pl.SetLogError(&event, input) } // get internalReference based on internalReferences_Id irData, err := uir.ReadDetailData(eir.ReadDetailDto{ Id: input.InternalReferences_Id}, &event, tx) if err != nil { return err } if *irData.Status_Code != erc.DACNew { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-state-mismatch", Detail: "internal references is approve", Raw: errors.New("internal references is approve"), } return pl.SetLogError(&event, input) } // update internal reference if err = uir.UpdateData(eir.UpdateDto{ Id: input.InternalReferences_Id, CreateDto: eir.CreateDto{ Status_Code: erc.DACCanceled, Nurse_Code: input.Nurse_Code}}, irData, &event, tx); err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCancelSwitchSpecialistMiddleware(cancelSwitchEncounter, &input); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", "status": "updated", }, Data: data.ToResponse(), }, nil } func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { var errMsg string inputLog := esync.SimxLogDto{ Payload: input, Method: erc.CCCreate, } if err != nil { // Run log-middleware errMsg = err.Error() inputLog.ErrMessage = &errMsg inputLog.IsSuccess = false // create log failed if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { return errMiddleware } return err } // create log success inputLog.IsSuccess = true if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { return err } return nil } func validateAuth(a auth.AuthInfo, roleAllowed []string, action string, event *pl.Event) error { // check if user has employee position if !a.HasEmployeePosition() { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "auth-forbidden", Detail: "missing employee position", Raw: errors.New("authentication failed"), } return pl.SetLogError(event, a) } // check only roleAllowed position is allowed to create encounter if a.Employee_Position_Code != nil && !pu.Contains(roleAllowed, *a.Employee_Position_Code) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "auth-forbidden", Detail: fmt.Sprintf("position '%s' is not allowed to %s", *a.Employee_Position_Code, action), Raw: errors.New("authentication failed"), } return pl.SetLogError(event, a) } return nil } func CreateWithPatient(input e.CreateWithPatientDto) (*d.Data, error) { var ( data e.Encounter recentSoapiDataforCopy []es.CreateDto err error ) event := pl.Event{ Feature: "Create", Source: source, } // Start log pl.SetLogInfo(&event, input, "started", "create") roleAllowed := []string{string(erg.EPCReg)} err = validateAuth(input.AuthInfo, roleAllowed, "create-encounter", &event) if err != nil { return nil, err } // validate rehab by bpjs if input.RefTypeCode == ere.RTCBpjs && input.Class_Code == ere.ECAmbulatory && ere.AmbulatoryClassCode(*input.SubClass_Code) == ere.ACCRehab { // get latest rehab data recentRehabData, err := getLatestRehabData(input.CreateDto, &event) if err != nil { return nil, err } // If recentRehabData is nil, then visitMode_Code = "adm" if recentRehabData != nil { // If recentRehabData is not nil, determine the visitMode_Code: // If the mode is "series", verify whether the visit count still remains // and whether the series has not expired. // If visitMode is "series", then get encounterAdm input.VisitMode_Code, input.RecentEncounterAdm, err = determineVisitMode(recentRehabData, input.CreateDto, &event) if err != nil { return nil, err } } else { input.VisitMode_Code = ere.VMCAdm } // When visitMode_Code is "series", load the associated SOAPI record to copy its values. if input.VisitMode_Code == ere.VMCSeries { // get data soapi recentSoapiDataforCopy, err = getSoapiEncounterAdm(*input.RecentEncounterAdm, &event) if err != nil { return nil, err } } } // check if patient is new in the hospital input.NewStatus, err = identifyPatientStatus(input.CreateDto) if err != nil { return nil, err } input.Adm_Employee_Id = input.AuthInfo.Employee_Id mwRunner := newMiddlewareRunner(&event, input.Sync) err = dg.I.Transaction(func(tx *gorm.DB) error { // create patient var patientId uint patientData, err := up.Create(input.Patient, tx) if err != nil { return err } if patientData != nil { patientId = patientData.Data.(ep.ResponseDto).Id } // create encounter input.Patient_Id = &patientId if resData, err := CreateData(input.CreateDto, &event, tx); err != nil { return err } else { data = *resData input.Id = data.Id } // insert ambulatory/emergency/inpatient err = insertdataClassCode(input.CreateDto, recentSoapiDataforCopy, &event, tx) if err != nil { return err } // insert vclaimReference if vr := input.VclaimReference; vr != nil { t, _ := time.Parse("2006-01-02", vr.TglRujukan) _, err = uv.CreateData(ev.CreateDto{ Encounter_Id: &data.Id, Date: &t, SrcCode: input.Ref_Number, SrcName: input.RefSource_Name, Number: &vr.NoSep}, &event, tx) if err != nil { return err } } dataEncounter, err := ReadDetailData(e.ReadDetailDto{ Id: data.Id, Includes: "Adm_Employee.User,Adm_Employee.Person," + "Patient.Person.Relatives," + "Patient.Person.VclaimMember,VclaimReference," + "Patient.Person.Contacts,Patient.Person.Addresses"}, &event, tx) if err != nil { return err } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunCreateWithPatientMiddleware(createWithPatientPreMw, dataEncounter); err != nil { return err } return nil }) if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.II{ "source": source, "structure": "single-data", "status": "created", }, Data: data.ToResponse(), }, nil }