feat (generate-file): general-consent pdf done
This commit is contained in:
@@ -125,11 +125,11 @@
|
||||
>
|
||||
<br /><span style="margin-left: 10px"
|
||||
>c) Anggota keluarga saya :
|
||||
{{ if eq (len .FamilyMembers) 0 }}
|
||||
{{ if eq (len .PatientRelatives_Name) 0 }}
|
||||
..........................................
|
||||
{{ else }}
|
||||
<ul style="margin:0; padding-left:20px;">
|
||||
{{ range $i, $name := .FamilyMembers }}
|
||||
<ul style="margin:0; padding-left:40px;">
|
||||
{{ range $i, $name := .PatientRelatives_Name }}
|
||||
{{ if lt $i 2 }}
|
||||
<li>{{ $name }}</li>
|
||||
{{ end }}
|
||||
@@ -247,7 +247,7 @@
|
||||
<table style="width: 100%; margin-top: 40px; text-align: center">
|
||||
<tr>
|
||||
<td>
|
||||
Malang, ............................................................
|
||||
Malang, {{ .Date }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -260,20 +260,66 @@
|
||||
border-collapse: collapse;
|
||||
"
|
||||
>
|
||||
<tr>
|
||||
<td>
|
||||
Pasien/keluarga/<br>penanggung jawab<br /><br /><br />.......................................<br />{{ .Responsible_Name }}
|
||||
</td>
|
||||
<td>
|
||||
Pemberi Informasi<br /><br /><br />.......................................<br />{{ .InformationGivenBy_Name }}
|
||||
</td>
|
||||
<td>
|
||||
Saksi I<br /><br /><br />.......................................<br /> {{ .Witness1_Name }}
|
||||
</td>
|
||||
<td>
|
||||
Saksi II<br /><br /><br />.......................................<br /> {{ .Witness2_Name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height:160px; vertical-align:top;">
|
||||
<td style="text-align:center; padding:0 10px;">
|
||||
<div style="margin-top:10px; height:45px;">
|
||||
Pasien/keluarga/<br>penanggung jawab
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; margin-bottom:20px;">
|
||||
.......................................
|
||||
</div>
|
||||
|
||||
<div style="height:20px;">
|
||||
{{ .Responsible_Name }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style="text-align:center; padding:0 10px;">
|
||||
<div style="margin-top:10px; height:45px;">
|
||||
Pemberi Informasi
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; margin-bottom:20px;">
|
||||
.......................................
|
||||
</div>
|
||||
|
||||
<div style="height:20px;">
|
||||
{{ .InformationGivenBy_Name }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style="text-align:center; padding:0 10px;">
|
||||
<div style="margin-top:10px; height:45px;">
|
||||
Saksi I
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; margin-bottom:20px;">
|
||||
.......................................
|
||||
</div>
|
||||
|
||||
<div style="height:20px;">
|
||||
{{ .Witness1_Name }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style="text-align:center; padding:0 10px;">
|
||||
<div style="margin-top:10px; height:45px;">
|
||||
Saksi II
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; margin-bottom:20px;">
|
||||
.......................................
|
||||
</div>
|
||||
|
||||
<div style="height:20px;">
|
||||
{{ .Witness2_Name }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
type CreateDto struct {
|
||||
Encounter_Id *uint `json:"-"`
|
||||
Encounter_Id *uint `json:"encounter_id"`
|
||||
Value *string `json:"value"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package generalconsent
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
// main entities
|
||||
e "simrs-vx/internal/domain/main-entities/general-consent"
|
||||
|
||||
ue "simrs-vx/internal/use-case/main-use-case/encounter"
|
||||
|
||||
pl "simrs-vx/pkg/logger"
|
||||
pu "simrs-vx/pkg/use-case-helper"
|
||||
|
||||
@@ -35,6 +38,11 @@ func Create(input e.CreateDto) (*d.Data, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if encounter is done
|
||||
if ue.IsDone(*input.Encounter_Id, &event, tx) {
|
||||
return errors.New("encounter is already done")
|
||||
}
|
||||
|
||||
if resData, err := CreateData(input, &event, tx); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -192,6 +200,11 @@ func Update(input e.UpdateDto) (*d.Data, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if encounter is done
|
||||
if ue.IsDone(*input.Encounter_Id, &event, tx) {
|
||||
return errors.New("encounter is already done")
|
||||
}
|
||||
|
||||
if err := UpdateData(input, data, &event, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
ugc "simrs-vx/internal/use-case/main-use-case/general-consent"
|
||||
|
||||
pl "simrs-vx/pkg/logger"
|
||||
pu "simrs-vx/pkg/use-case-helper"
|
||||
|
||||
dg "github.com/karincake/apem/db-gorm-pg"
|
||||
d "github.com/karincake/dodol"
|
||||
@@ -31,11 +32,17 @@ func Generate(input GenerateDto) (*d.Data, error) {
|
||||
// general-consent
|
||||
case ere.DTCGC:
|
||||
// get value from general consent by ref_id
|
||||
gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *input.Ref_Id}, &event, nil)
|
||||
gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: *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 {
|
||||
@@ -54,6 +61,8 @@ func Generate(input GenerateDto) (*d.Data, error) {
|
||||
|
||||
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)
|
||||
|
||||
@@ -9,19 +9,21 @@ import (
|
||||
|
||||
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"
|
||||
pm "simrs-vx/pkg/minio-helper"
|
||||
pp "simrs-vx/pkg/pdf-helper"
|
||||
pu "simrs-vx/pkg/use-case-helper"
|
||||
)
|
||||
|
||||
// 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("./public/%s/%d/%s", input.EntityType_Code, *input.Ref_Id, input.Type_Code))
|
||||
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)
|
||||
fPath := fmt.Sprintf("%s/%s-%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType)
|
||||
|
||||
templatePath := docscfg.O.GetPath() + string(input.TemplateName)
|
||||
|
||||
@@ -44,7 +46,7 @@ func generateFile(input GenerateDto, templateData any) (string, error) {
|
||||
|
||||
bucketName := input.EntityType_Code
|
||||
|
||||
objectName := fmt.Sprintf("%v/%s-%d", *input.Ref_Id, input.Type_Code, time.Now().UnixNano())
|
||||
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,
|
||||
@@ -81,3 +83,12 @@ func generatePDF(input GeneratePDFdto) 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
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ type GeneralConsentPDF struct {
|
||||
InformationGivenBy_Name string `json:"informationGivenBy_name"`
|
||||
Witness1_Name string `json:"witness1_name"`
|
||||
Witness2_Name string `json:"witness2_name"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
type GenerateDto struct {
|
||||
@@ -19,6 +20,7 @@ type GenerateDto struct {
|
||||
Type_Code ere.DocTypeCode `json:"type_code" validate:"required"`
|
||||
FormatType erc.DocFormatTypeCode `json:"formatType"`
|
||||
TemplateName TemplateDocsName `json:"-"`
|
||||
Encounter_Id *uint `json:"-"`
|
||||
}
|
||||
|
||||
type GeneratePDFdto struct {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package convhelper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// check string pointer, if nil return default value
|
||||
func StrConvDefault(f *string, def string) string {
|
||||
if f == nil {
|
||||
return def
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
// check string pointer, if nil return empty string
|
||||
func StrConvEmpty(f *string) string {
|
||||
return StrConvDefault(f, "-")
|
||||
}
|
||||
|
||||
func ByteConvStr(f *byte) string {
|
||||
if f != nil {
|
||||
return strconv.Itoa(int(*f))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func Float32Conv(f *float32) float32 {
|
||||
if f == nil {
|
||||
return 0
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
func StrRelConv(b bool, d string) string {
|
||||
if b {
|
||||
return d
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func UintToString(f interface{}) *string {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var t string
|
||||
switch v := f.(type) {
|
||||
case uint:
|
||||
t = strconv.FormatUint(uint64(v), 10)
|
||||
case uint8:
|
||||
t = strconv.FormatUint(uint64(v), 10)
|
||||
case uint16:
|
||||
t = strconv.FormatUint(uint64(v), 10)
|
||||
case uint32:
|
||||
t = strconv.FormatUint(uint64(v), 10)
|
||||
case uint64:
|
||||
t = strconv.FormatUint(v, 10)
|
||||
// Handle pointer types
|
||||
case *uint:
|
||||
if v != nil {
|
||||
t = strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
case *uint8:
|
||||
if v != nil {
|
||||
t = strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
case *uint16:
|
||||
if v != nil {
|
||||
t = strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
case *uint32:
|
||||
if v != nil {
|
||||
t = strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
case *uint64:
|
||||
if v != nil {
|
||||
t = strconv.FormatUint(*v, 10)
|
||||
}
|
||||
default:
|
||||
return nil // Unsupported type
|
||||
}
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func BoolToString(f *bool) *string {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
t := strconv.FormatBool(*f)
|
||||
return &t
|
||||
}
|
||||
|
||||
func TimeToString(f *time.Time) *string {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
t := f.Format("2006-01-02 15:04:05")
|
||||
return &t
|
||||
}
|
||||
|
||||
// Handling gorm.DeletedAt
|
||||
func DeletedAtToString(deletedAt *gorm.DeletedAt) *string {
|
||||
if deletedAt == nil || !deletedAt.Valid {
|
||||
return nil
|
||||
}
|
||||
return TimeToString(&deletedAt.Time)
|
||||
}
|
||||
|
||||
func BoolToFloat64(b bool) float64 {
|
||||
if b {
|
||||
return 1.0
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func Float64ToBool(f float64) bool {
|
||||
return f == 1.0
|
||||
}
|
||||
|
||||
func StringToBool(s string) bool {
|
||||
return s == "1"
|
||||
}
|
||||
|
||||
func StringToFloat32(s string) float32 {
|
||||
f, _ := strconv.ParseFloat(s, 32)
|
||||
return float32(f)
|
||||
}
|
||||
|
||||
func StringToFloat64(s string) float64 {
|
||||
f, _ := strconv.ParseFloat(s, 64)
|
||||
return f
|
||||
}
|
||||
|
||||
func Float64ToString(f float64) string {
|
||||
return fmt.Sprintf("%.2f", f)
|
||||
}
|
||||
|
||||
func StringToUint64(s string) *uint64 {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
u, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &u
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package usecasehelper
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -166,3 +167,44 @@ func index[S ~[]E, E comparable](s S, v E) int {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func FormatIndonesianDate(t time.Time) string {
|
||||
monthNames := [...]string{
|
||||
"", // dummy index 0
|
||||
"Januari",
|
||||
"Februari",
|
||||
"Maret",
|
||||
"April",
|
||||
"Mei",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"Agustus",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Desember",
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d %s %d", t.Day(), monthNames[int(t.Month())], t.Year())
|
||||
}
|
||||
|
||||
func GetLastTwoPathSegments(s string) string {
|
||||
u, err := url.Parse(s)
|
||||
var path string
|
||||
|
||||
if err == nil && u.Path != "" {
|
||||
path = u.Path
|
||||
} else {
|
||||
path = s
|
||||
}
|
||||
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
n := len(parts)
|
||||
|
||||
if n >= 2 {
|
||||
return parts[n-2] + "/" + parts[n-1]
|
||||
}
|
||||
|
||||
// fallback: return entire string if less than 2 segments
|
||||
return strings.Trim(path, "/")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user