package generatefile import ( "encoding/json" "errors" "fmt" "mime" "path/filepath" "time" evs "simrs-vx/internal/domain/bpjs-entities/vclaim-sep" evscl "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" ee "simrs-vx/internal/domain/main-entities/encounter" egc "simrs-vx/internal/domain/main-entities/general-consent" er "simrs-vx/internal/domain/main-entities/resume" uvs "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep" uvscl "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep-control-letter" ue "simrs-vx/internal/use-case/main-use-case/encounter" ugc "simrs-vx/internal/use-case/main-use-case/general-consent" ur "simrs-vx/internal/use-case/main-use-case/resume" erc "simrs-vx/internal/domain/references/common" docscfg "simrs-vx/internal/infra/docs-cfg" pc "simrs-vx/pkg/conv-helper" pf "simrs-vx/pkg/file-helper" pl "simrs-vx/pkg/logger" pm "simrs-vx/pkg/minio-helper" pp "simrs-vx/pkg/pdf-helper" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" ) // generate temporary file, upload to minio, generate public url, delete temporary file func generateFile(input GenerateDto, templateData any) (string, error) { newPath, err := pf.PathToSaveFile(fmt.Sprintf("./temporary/%s", input.EntityType_Code)) if err != nil { return "", err } fPath := fmt.Sprintf("%s/%s-%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType) templatePath := docscfg.O.GetPath() + string(input.TemplateName) switch input.FormatType { case erc.DFTCPDF: if err := generatePDF(GeneratePDFdto{ TemplatePath: templatePath, TemplateData: templateData, PdfPath: fPath, UseA5Lanscape: input.UseA5Lanscape, }); err != nil { return "", err } case erc.DFTCTXLSX: // TODO: generate xlsx case erc.DFTCTCSV: // TODO: generate csv default: return "", errors.New("invalid format type") } bucketName := input.EntityType_Code objectName := fmt.Sprintf("%s/%s-%d.%s", *pc.UintToString(input.Encounter_Id), input.Type_Code, time.Now().UnixNano(), input.FormatType) pdfUpload := pm.UploadPathInput{ BucketName: string(bucketName), Name: objectName, Path: fPath, ContentType: mime.TypeByExtension(filepath.Ext(fPath)), } // create bucket if not exist, create object in bucket info, err := pm.I.FPutObject(pdfUpload) if err != nil { return "", err } // generate public url urlPub := pm.I.GenerateUrl(info.Bucket, info.Key) if err := pf.DeleteFolder(fPath); err != nil { return "", err } return urlPub, nil } func generatePDF(input GeneratePDFdto) error { // parse template data into html template r := pp.NewRequestPdf("") if err := r.ParseTemplate(input.TemplatePath, input.TemplateData); err == nil { _, err := r.GenerateByCommand(input.PdfPath, input.TemplatePath, input.UseA5Lanscape) if err != nil { return errors.New("generate pdf by command error : " + err.Error()) } } else { return errors.New("parse template error : " + err.Error()) } return nil } func removeFile(bucket, fileUrl string) error { fPath := pu.GetLastTwoPathSegments(fileUrl) err := pm.I.RemoveObject(bucket, fPath) if err != nil { return err } return nil } // generate general consent func generateGC(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) { // get value from general consent by ref_id gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id))}, &event) if err != nil { return nil, err } if gc.FileUrl != nil { if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil { return nil, err } } // map template data templateData := GeneralConsentPDF{} if gc.Value != nil { err := json.Unmarshal([]byte(*gc.Value), &templateData) if err != nil { event.ErrInfo = pl.ErrorInfo{ Code: "data-unmarshal-fail", Detail: err.Error(), Raw: err, } return nil, err } } else { return nil, errors.New("no value in this general consent") } input.FormatType = erc.DFTCPDF input.TemplateName = TDNGC input.Encounter_Id = gc.Encounter_Id templateData.Date = pu.FormatIndonesianDate(gc.CreatedAt) // generate file urlPub, err := generateFile(input, templateData) if err != nil { return nil, err } gc.FileUrl = &urlPub if err := tx.Save(&gc).Error; err != nil { return nil, err } response := ResponseDto{ FileUrl: urlPub, } return &response, nil } func generateCL(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) { // get value from control letter by ref_id cl, err := uvscl.ReadDetailData(evscl.ReadDetailDto{Number: input.Ref_Id}, &event) if err != nil { if !pu.IsDataNotFoundError(err) { return nil, err } } if cl != nil && cl.FileUrl != nil { if err := removeFile(string(input.EntityType_Code), *cl.FileUrl); err != nil { return nil, err } } // map template data clData := VclaimControlLetter{} if input.Data != nil { err := json.Unmarshal([]byte(*input.Data), &clData) if err != nil { event.ErrInfo = pl.ErrorInfo{ Code: "data-unmarshal-fail", Detail: err.Error(), Raw: err, } return nil, err } } else { return nil, errors.New("there is no data to be used") } if cl == nil { createCL := evscl.CreateDto{ VclaimSep_Number: &clData.VclaimSep.Number, Number: &clData.Number, Value: input.Data, } if cl, err = uvscl.CreateData(createCL, &event, tx); err != nil { return nil, err } } // get encounter id by vclaim sep number vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: &clData.VclaimSep.Number}, &event) if err != nil { return nil, err } input.FormatType = erc.DFTCPDF input.TemplateName = TDNCL input.Encounter_Id = vs.Encounter_Id input.UseA5Lanscape = true templateData := clData.generateTemplateData() // generate file urlPub, err := generateFile(input, templateData) if err != nil { return nil, err } cl.FileUrl = &urlPub if err := tx.Save(&cl).Error; err != nil { return nil, err } response := ResponseDto{ FileUrl: urlPub, } return &response, nil } func generateResume(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) { // get value from resume by ref_id includes := "Doctor.Employee.Person" r, err := ur.ReadDetailData(er.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id)), Includes: includes}, &event) if err != nil { return nil, err } if !r.IsValidated() { return nil, errors.New("resume is not validated") } if r.FileUrl != nil { if err := removeFile(string(input.EntityType_Code), *r.FileUrl); err != nil { return nil, err } } templateData, err := generateResumeTemplateData(*r, event, tx) if err != nil { return nil, err } input.FormatType = erc.DFTCPDF input.TemplateName = TDNR input.Encounter_Id = r.Encounter_Id // generate file urlPub, err := generateFile(input, templateData) if err != nil { return nil, err } r.FileUrl = &urlPub if err := tx.Save(&r).Error; err != nil { return nil, err } response := ResponseDto{ FileUrl: urlPub, } return &response, nil } func generateResumeTemplateData(resume er.Resume, event pl.Event, tx *gorm.DB) (*ResumePDF, error) { // get encounter includes := "Patient,Patient.Person,Patient.Person.BirthRegency,Patient.Person.Contacts" encounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *resume.Encounter_Id, Includes: includes}, &event) if err != nil { return nil, err } // map template data rData := er.ValueDto{} if resume.Value != nil { err := json.Unmarshal([]byte(*resume.Value), &rData) if err != nil { event.ErrInfo = pl.ErrorInfo{ Code: "data-unmarshal-fail", Detail: err.Error(), Raw: err, } return nil, err } } else { return nil, errors.New("there is no data to be used") } templateData := ResumePDF{} templateData.MedicalRecord = *encounter.Patient.Number templateData.NIK = *encounter.Patient.Person.ResidentIdentityNumber templateData.Name = encounter.Patient.Person.Name templateData.BirthPlaceDate = pu.FormatPlaceAndDate(encounter.Patient.Person.BirthRegency.Name, *encounter.Patient.Person.BirthDate) templateData.Gender = encounter.Patient.Person.GenderString() templateData.Phone = encounter.Patient.Person.GetPhoneNumber() // templateData.Class = get from vclaim sep eg. III // templateData.Unit = get from vclaim sep eg. R.KERINCI - KLS 3 templateData.Doctor_Name = resume.Doctor.Employee.Person.FullName() templateData.StartedAt = *pc.TimeToStringWithFormat(encounter.StartedAt, "") templateData.FinishedAt = *pc.TimeToStringWithFormat(encounter.FinishedAt, "") templateData.DiagnosisIn = rData.Assessment.DiagnosisIn templateData.Diagnosis = rData.Diagnosis.SecondaryDiagnosis templateData.MainComplaint = rData.Assessment.MainComplaint templateData.MedicalHistory = rData.Assessment.MedicalHistory templateData.PhysicalExamination = rData.Assessment.PhysicalExamination templateData.SupportingExamination = rData.Supporting.Notes templateData.MedicalActions = rData.Action.AdditionalActions templateData.MedicalAction = rData.Action.MedicalActions templateData.Consultations = rData.Consultation.Consultations templateData.Allergy = rData.Pharmacy.AllergySpecialConditions templateData.ConsciousnessLevel = rData.Discharge.ConsciousnessLevel templateData.BloodPressure = fmt.Sprintf("%.0f/%.0f", rData.Discharge.BloodPressureSystolic, rData.Discharge.BloodPressureDiastolic) templateData.BodyTemperature = rData.Discharge.BodyTemperature templateData.RespirationRate = rData.Discharge.RespirationRate templateData.HeartRate = rData.Discharge.HeartRate templateData.PainScale = rData.Discharge.PainScale templateData.ConditionOnDischarge = rData.Management.ConditionOnDischarge templateData.DischargeMethod = rData.Management.DischargeMethod templateData.Medications = rData.Medication.Medications templateData.Date = pu.GetTimeNow().Format("02-01-2006") return &templateData, nil }