feat (generate-file): screening form a tested and ok, form b not fully tested
This commit is contained in:
@@ -56,17 +56,17 @@
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<td style="width: 9rem;">No. RM</td>
|
||||
<td style="width: 9rem;"><b>No. RM</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td>{{ .MedicalRecord }}</td>
|
||||
<td><b>{{ .MedicalRecord }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nama</td>
|
||||
<td><b>Nama</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td><b>{{ .Name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tanggal Lahir</td>
|
||||
<td><b>Tanggal Lahir</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td>{{ .BirthDate }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -33,94 +33,76 @@
|
||||
</head>
|
||||
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.4">
|
||||
<table style="margin-top: 1rem;">
|
||||
<tr>
|
||||
<td style="width: 9rem;">
|
||||
Tanggal Terbit
|
||||
</td>
|
||||
<td class="data-colon">: </td>
|
||||
<td>{{ .Repalcable }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
No. RM
|
||||
</td>
|
||||
<td class="data-colon">: </td>
|
||||
<td>{{ .Repalcable }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Nama
|
||||
</td>
|
||||
<td class="data-colon">: </td>
|
||||
<td>{{ .Repalcable }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Tanggal Lahir
|
||||
</td>
|
||||
<td class="data-colon">: </td>
|
||||
<td>{{ .Repalcable }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="margin: 20px 0" />
|
||||
<h1 style="
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
<!-- Tanggal Terbit (top-right) -->
|
||||
<div style="
|
||||
text-align: right;
|
||||
margin-right: 2rem;
|
||||
font-size: 0.95rem;
|
||||
margin-top: 0.5rem;
|
||||
">
|
||||
FORM B
|
||||
</h1>
|
||||
Tanggal Terbit : {{ .IssuedDate }}
|
||||
</div>
|
||||
|
||||
<table class="border-table" style="margin-top: 1rem;">
|
||||
<!-- Patient Info Box -->
|
||||
<table
|
||||
style="
|
||||
margin-top: 1rem;
|
||||
margin-left: auto;
|
||||
margin-right: 2rem; /* space from right like example */
|
||||
border: 1px solid #000;
|
||||
padding: 0.5rem 1rem;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0.3rem 0.2rem;
|
||||
width: auto;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<th style="width: 2rem;">
|
||||
NO
|
||||
</th>
|
||||
<th>
|
||||
Tanggal/jam
|
||||
</th>
|
||||
<th>
|
||||
Implementasi, Monitoring, Fasilitasi, koordinasi, komunikasi dan kolaborasi, advokasi, hasil pelayanan,
|
||||
Terminasi
|
||||
</th>
|
||||
<th>
|
||||
MPP
|
||||
</th>
|
||||
<td style="width: 9rem;"><b>No. RM</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td><b>{{ .MedicalRecord }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;">
|
||||
1
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
2025-10-08 16:08:18
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><b>Implementasi: </b>Pemantauan harian terhadap penggunaan alat medis (oksigen dan alat bantu
|
||||
jalan) serta kepatuhan terapi.</li>
|
||||
<li><b>Monitoring: </b>Observasi respon pasien terhadap terapi dan edukasi keluarga untuk perawatan
|
||||
mandiri di rumah.</li>
|
||||
<li><b>Fasilitasi: </b>Menyediakan alat bantu sesuai kebutuhan dan menghubungkan pasien dengan
|
||||
layanan rehabilitasi medik.</li>
|
||||
<li><b>Koordinasi: </b>Melibatkan dokter penanggung jawab, perawat, dan keluarga dalam penyusunan
|
||||
rencana tindak lanjut.</li>
|
||||
<li><b>Komunikasi dan Kolaborasi: </b>Diskusi rutin antar profesi untuk evaluasi perkembangan
|
||||
pasien.</li>
|
||||
<li><b>Advokasi: </b>Memberikan dukungan informasi dan hak pasien terkait rencana pulang dan
|
||||
kesinambungan terapi.</li>
|
||||
<li><b>Hasil Pelayanan: </b>Pasien menunjukkan perbaikan fungsi mobilitas, tingkat kepatuhan terapi
|
||||
meningkat.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td style="width: 3rem;padding: 0.3rem;text-align: center;">
|
||||
GATOT SUBROTO,AMd.Kep
|
||||
</td>
|
||||
<td><b>Nama</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td><b>{{ .Name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Tanggal Lahir</b></td>
|
||||
<td class="data-colon">:</td>
|
||||
<td>{{ .BirthDate }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="border-table"
|
||||
style="margin-top: 1rem; width: 100%; table-layout: fixed;">
|
||||
|
||||
<tr>
|
||||
<th style="width: 3rem; text-align:center;">NO</th>
|
||||
<th style="width: 9rem; text-align:center;">Tanggal/jam</th>
|
||||
<th style="width: auto;">
|
||||
Implementasi, Monitoring, Fasilitasi, koordinasi, komunikasi dan kolaborasi,
|
||||
advokasi, hasil pelayanan, Terminasi
|
||||
</th>
|
||||
<th style="width: 9rem; text-align:center;">MPP</th>
|
||||
</tr>
|
||||
|
||||
{{ range .FormB }}
|
||||
<tr>
|
||||
<td style="text-align: center;">{{ .Number }}</td>
|
||||
<td style="text-align: center;">{{ .Date }}</td>
|
||||
|
||||
<td style="white-space: normal; word-wrap: break-word;">
|
||||
{{ .Value }}
|
||||
</td>
|
||||
|
||||
<td style="text-align: center; white-space: normal;">
|
||||
{{ .Employee_Name }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
|
||||
</table>
|
||||
|
||||
<div style="height: 3rem;"></div>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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", "<br>", -1))
|
||||
},
|
||||
"safeHTML": func(text string) template.HTML {
|
||||
return template.HTML(text)
|
||||
},
|
||||
}
|
||||
t, err := template.New(fileName).Funcs(funcs).ParseFiles(templatePath)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user