From 5975721daf1defff62cbb93efce8b23025a27c7d Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 3 Dec 2025 16:10:23 +0700 Subject: [PATCH] feat (generate-file): screening form a tested and ok, form b not fully tested --- assets/docs/screening-form-a.html | 8 +- assets/docs/screening-form-b.html | 142 ++++++++---------- .../domain/main-entities/screening/dto.go | 8 +- .../domain/main-entities/screening/entity.go | 4 +- .../interface/main-handler/main-handler.go | 4 +- .../main-handler/screening/handler.go | 34 ++--- .../main-use-case/generate-file/case.go | 6 + .../main-use-case/generate-file/helper.go | 139 ++++++++++++++--- .../main-use-case/generate-file/tycovar.go | 8 + .../use-case/main-use-case/screening/lib.go | 4 +- pkg/pdf-helper/pdf-helper.go | 3 + 11 files changed, 228 insertions(+), 132 deletions(-) diff --git a/assets/docs/screening-form-a.html b/assets/docs/screening-form-a.html index a838c111..4f34b876 100644 --- a/assets/docs/screening-form-a.html +++ b/assets/docs/screening-form-a.html @@ -56,17 +56,17 @@ " > - No. RM + No. RM : - {{ .MedicalRecord }} + {{ .MedicalRecord }} - Nama + Nama : {{ .Name }} - Tanggal Lahir + Tanggal Lahir : {{ .BirthDate }} diff --git a/assets/docs/screening-form-b.html b/assets/docs/screening-form-b.html index 1ae8a07c..9abc94ce 100644 --- a/assets/docs/screening-form-b.html +++ b/assets/docs/screening-form-b.html @@ -33,94 +33,76 @@ - - - - - - - - - - - - - - - - - - - - - -
- Tanggal Terbit - : {{ .Repalcable }}
- No. RM - : {{ .Repalcable }}
- Nama - : {{ .Repalcable }}
- Tanggal Lahir - : {{ .Repalcable }}
- -
-

- FORM B -

+ Tanggal Terbit : {{ .IssuedDate }} + - + +
- - - - + + + - - - - + + + + + + + +
- NO - - Tanggal/jam - - Implementasi, Monitoring, Fasilitasi, koordinasi, komunikasi dan kolaborasi, advokasi, hasil pelayanan, - Terminasi - - MPP - No. RM:{{ .MedicalRecord }}
- 1 - - 2025-10-08 16:08:18 - -
    -
  • Implementasi: Pemantauan harian terhadap penggunaan alat medis (oksigen dan alat bantu - jalan) serta kepatuhan terapi.
  • -
  • Monitoring: Observasi respon pasien terhadap terapi dan edukasi keluarga untuk perawatan - mandiri di rumah.
  • -
  • Fasilitasi: Menyediakan alat bantu sesuai kebutuhan dan menghubungkan pasien dengan - layanan rehabilitasi medik.
  • -
  • Koordinasi: Melibatkan dokter penanggung jawab, perawat, dan keluarga dalam penyusunan - rencana tindak lanjut.
  • -
  • Komunikasi dan Kolaborasi: Diskusi rutin antar profesi untuk evaluasi perkembangan - pasien.
  • -
  • Advokasi: Memberikan dukungan informasi dan hak pasien terkait rencana pulang dan - kesinambungan terapi.
  • -
  • Hasil Pelayanan: Pasien menunjukkan perbaikan fungsi mobilitas, tingkat kepatuhan terapi - meningkat.
  • -
-
- GATOT SUBROTO,AMd.Kep - Nama:{{ .Name }}
Tanggal Lahir:{{ .BirthDate }}
+ + + + + + + + + + {{ range .FormB }} + + + + + + + + + {{ end }} + +
NOTanggal/jam + Implementasi, Monitoring, Fasilitasi, koordinasi, komunikasi dan kolaborasi, + advokasi, hasil pelayanan, Terminasi + MPP
{{ .Number }}{{ .Date }} + {{ .Value }} + + {{ .Employee_Name }} +
+
diff --git a/internal/domain/main-entities/screening/dto.go b/internal/domain/main-entities/screening/dto.go index b001af39..0c4310ed 100644 --- a/internal/domain/main-entities/screening/dto.go +++ b/internal/domain/main-entities/screening/dto.go @@ -27,6 +27,7 @@ type CreateDto struct { type ReadListDto struct { FilterDto Includes string `json:"includes"` + Sort string `json:"sort"` Pagination ecore.Pagination } @@ -38,16 +39,17 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` + Includes string `json:"includes"` } type UpdateDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` CreateDto } type DeleteDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` } type MetaDto struct { diff --git a/internal/domain/main-entities/screening/entity.go b/internal/domain/main-entities/screening/entity.go index fa650165..007573c4 100644 --- a/internal/domain/main-entities/screening/entity.go +++ b/internal/domain/main-entities/screening/entity.go @@ -19,4 +19,6 @@ type Screening struct { FileUrl *string `json:"fileUrl" gorm:"size:1024"` } -// func (d Screening) UnMarshal(data []byte) (error {} +func (d Screening) IsFormA() bool { + return d.Type == erc.SFTCA +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 8864fcbb..55342b0f 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -316,7 +316,9 @@ func SetRoutes() http.Handler { "PATCH /{id}/validate": resume.Validate, }) hk.GroupRoutes("/v1/screening", r, auth.GuardMW, hk.MapHandlerFunc{ - "POST /": screening.O.Create, + "POST /": screening.O.Create, + "GET /": screening.O.GetList, + "GET /{id}": screening.O.GetDetail, }) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/interface/main-handler/screening/handler.go b/internal/interface/main-handler/screening/handler.go index 29ea5c73..a6866cc9 100644 --- a/internal/interface/main-handler/screening/handler.go +++ b/internal/interface/main-handler/screening/handler.go @@ -4,7 +4,7 @@ import ( "net/http" rw "github.com/karincake/risoles" - // sf "github.com/karincake/semprit" + sf "github.com/karincake/semprit" // ua "github.com/karincake/tumpeng/auth/svc" @@ -34,23 +34,23 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { rw.DataResponse(w, res, err) } -// func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { -// dto := e.ReadListDto{} -// sf.UrlQueryParam(&dto, *r.URL) -// res, err := u.ReadList(dto) -// rw.DataResponse(w, res, err) -// } +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} -// func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { -// id := rw.ValidateInt(w, "id", r.PathValue("id")) -// if id <= 0 { -// return -// } -// dto := e.ReadDetailDto{} -// dto.Id = uint16(id) -// res, err := u.ReadDetail(dto) -// rw.DataResponse(w, res, err) -// } +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} // func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { // id := rw.ValidateInt(w, "id", r.PathValue("id")) diff --git a/internal/use-case/main-use-case/generate-file/case.go b/internal/use-case/main-use-case/generate-file/case.go index 7c657640..b5103746 100644 --- a/internal/use-case/main-use-case/generate-file/case.go +++ b/internal/use-case/main-use-case/generate-file/case.go @@ -51,6 +51,12 @@ func Generate(input GenerateDto) (*d.Data, error) { return err } + // screening + case ere.DTCScreening: + response, err = generateScreening(input, event, tx) + if err != nil { + return err + } default: return errors.New("invalid type code") } diff --git a/internal/use-case/main-use-case/generate-file/helper.go b/internal/use-case/main-use-case/generate-file/helper.go index 76e4f5ee..a0e7c1e2 100644 --- a/internal/use-case/main-use-case/generate-file/helper.go +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -20,7 +20,9 @@ import ( 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" + us "simrs-vx/internal/use-case/main-use-case/screening" + ercl "simrs-vx/internal/domain/references/clinical" erc "simrs-vx/internal/domain/references/common" docscfg "simrs-vx/internal/infra/docs-cfg" @@ -341,41 +343,130 @@ func generateResumeTemplateData(resume er.Resume, event pl.Event, tx *gorm.DB) ( return &templateData, nil } -func generateScreeningFormATemplateData(screening es.Screening, event pl.Event, tx *gorm.DB) (*ScreeningPDF, error) { +func generateScreeningTemplateData(screenings []es.Screening, event pl.Event, tx *gorm.DB) (*ScreeningPDF, error) { // get encounter includes := "Patient,Patient.Person" - encounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *screening.Encounter_Id, Includes: includes}, &event) + encounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *screenings[0].Encounter_Id, Includes: includes}, &event) if err != nil { return nil, err } - // map template data - sData := es.FormA{} - if screening.Value != nil { - err := json.Unmarshal([]byte(*screening.Value), &sData) - if err != nil { - event.ErrInfo = pl.ErrorInfo{ - Code: "data-unmarshal-fail", - Detail: err.Error(), - Raw: err, + switch screenings[0].Type { + case ercl.SFTCA: + // map template data + sData := es.FormA{} + if screenings[0].Value != nil { + err := json.Unmarshal([]byte(*screenings[0].Value), &sData) + 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 := ScreeningPDF{} + templateData.IssuedDate = pu.GetTimeNow().Format("2006-01-02 15:04:05") + templateData.MedicalRecord = *encounter.Patient.Number + templateData.Name = encounter.Patient.Person.FullName() + templateData.BirthDate = encounter.Patient.Person.BirthDate.Format("2006-01-02 15:04:05") + templateData.Employee_Name = screenings[0].Employee.Person.FullName() + templateData.EarlyMedic = sData.Screening.SelectedScreeningLabels() + templateData.Assessment = sData.AssessmentDetail + templateData.ProblemIdentification = sData.ProblemIdentification.SelectedProblemLabels() + templateData.Planning = sData.PlanningDetail + templateData.Date = screenings[0].CreatedAt.Format("2006-01-02 15:04:05") + + return &templateData, nil + case ercl.SFTCB: + // map template data + templateData := ScreeningPDF{} + templateData.IssuedDate = pu.GetTimeNow().Format("2006-01-02 15:04:05") + templateData.MedicalRecord = *encounter.Patient.Number + templateData.Name = encounter.Patient.Person.FullName() + templateData.BirthDate = encounter.Patient.Person.BirthDate.Format("2006-01-02 15:04:05") + for k, v := range screenings { + templateData.FormB = append(templateData.FormB, ScreeningFormBPDF{ + Number: k + 1, + Date: v.CreatedAt.Format("2006-01-02 15:04:05"), + Employee_Name: v.Employee.Person.FullName(), + Value: *v.Value, + }) + } + + return &templateData, nil + } + return nil, errors.New("invalid screening type") +} + +func generateScreening(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) { + // get value from resume by ref_id + includes := "Employee.Person" + s, err := us.ReadDetailData(es.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id)), Includes: includes}, &event) + if err != nil { + return nil, err + } + + var screenings []es.Screening + var templateData *ScreeningPDF + if s.IsFormA() { + if s.FileUrl != nil { + if err := removeFile(string(input.EntityType_Code), *s.FileUrl); err != nil { + return nil, err + } + } + screenings = append(screenings, *s) + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNSA + input.Encounter_Id = s.Encounter_Id + templateData, err = generateScreeningTemplateData(screenings, event, tx) + if err != nil { return nil, err } } else { - return nil, errors.New("there is no data to be used") + includes := "Employee.Person" + sort := "CreatedAt:ASC" + ss, _, err := us.ReadListData(es.ReadListDto{FilterDto: es.FilterDto{Encounter_Id: s.Encounter_Id, Type: ercl.SFTCB}, Includes: includes, Sort: sort}, &event, tx) + if err != nil { + return nil, err + } + screenings = append(screenings, ss...) + input.FormatType = erc.DFTCPDF + input.TemplateName = TDNSB + input.Encounter_Id = s.Encounter_Id + templateData, err = generateScreeningTemplateData(screenings, event, tx) + if err != nil { + return nil, err + } } - templateData := ScreeningPDF{} - templateData.IssuedDate = pu.GetTimeNow().Format("2006-01-02 15:04:05") - templateData.MedicalRecord = *encounter.Patient.Number - templateData.Name = encounter.Patient.Person.FullName() - templateData.BirthDate = encounter.Patient.Person.BirthDate.Format("2006-01-02 15:04:05") - templateData.Employee_Name = screening.Employee.Person.FullName() - templateData.EarlyMedic = sData.Screening.SelectedScreeningLabels() - templateData.Assessment = sData.AssessmentDetail - templateData.ProblemIdentification = sData.ProblemIdentification.SelectedProblemLabels() - templateData.Planning = sData.PlanningDetail - templateData.Date = screening.CreatedAt.Format("2006-01-02 15:04:05") + // generate file + urlPub, err := generateFile(input, templateData) + if err != nil { + return nil, err + } - return &templateData, nil + if len(screenings) > 1 { + for i := range screenings { + screenings[i].FileUrl = &urlPub + } + if err := tx.Save(&screenings).Error; err != nil { + return nil, err + } + } else { + s.FileUrl = &urlPub + if err := tx.Save(&s).Error; err != nil { + return nil, err + } + } + + response := ResponseDto{ + FileUrl: urlPub, + } + + return &response, nil } diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index d5a7220e..a8fd5ef5 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -83,6 +83,14 @@ type ScreeningPDF struct { Assessment string ProblemIdentification []string Planning string + FormB []ScreeningFormBPDF +} + +type ScreeningFormBPDF struct { + Number int + Date string + Employee_Name string + Value string } type GenerateDto struct { diff --git a/internal/use-case/main-use-case/screening/lib.go b/internal/use-case/main-use-case/screening/lib.go index 413e84d2..31ed6ee1 100644 --- a/internal/use-case/main-use-case/screening/lib.go +++ b/internal/use-case/main-use-case/screening/lib.go @@ -53,7 +53,7 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Sc Scopes(gh.Filter(input.FilterDto)). Count(&count). Scopes(gh.Paginate(input, &pagination)). - Order("\"CreatedAt\" DESC") + Scopes(gh.Sort(input.Sort)) if err := tx.Find(&data).Error; err != nil { if err == gorm.ErrRecordNotFound { @@ -81,7 +81,7 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e tx = dg.I } - if err := tx.First(&data, input.Id).Error; err != nil { + if err := tx.Scopes(gh.Preload(input.Includes)).First(&data, input.Id).Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { return nil, processedErr } diff --git a/pkg/pdf-helper/pdf-helper.go b/pkg/pdf-helper/pdf-helper.go index 9878ecbd..9b1dc129 100644 --- a/pkg/pdf-helper/pdf-helper.go +++ b/pkg/pdf-helper/pdf-helper.go @@ -33,6 +33,9 @@ func (r *RequestPdf) ParseTemplate(templatePath string, data interface{}) error "nl2br": func(text string) template.HTML { return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "
", -1)) }, + "safeHTML": func(text string) template.HTML { + return template.HTML(text) + }, } t, err := template.New(fileName).Funcs(funcs).ParseFiles(templatePath) if err != nil {