Merge branch 'dev' of https://github.com/dikstub-rssa/simrs-be into feat/sync-from-simx-160

# Conflicts:
#	internal/use-case/main-use-case/generate-file/tycovar.go
This commit is contained in:
vanilia
2025-11-19 16:13:30 +07:00
36 changed files with 1702 additions and 238 deletions
@@ -1,7 +1,7 @@
package encounter_document
import (
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
// internal - domain - base-entities
ecore "simrs-vx/internal/domain/base-entities/core"
@@ -10,7 +10,7 @@ import (
type CreateDto struct {
Encounter_Id *uint `json:"encounter_id"`
Type_Code eru.DocTypeCode `json:"type_code"`
Type_Code ere.DocTypeCode `json:"type_code"`
Name string `json:"name"`
FilePath string `json:"filePath"`
Filename string `json:"-"`
@@ -25,7 +25,7 @@ type ReadListDto struct {
type FilterDto struct {
Encounter_Id *uint `json:"encounter-id"`
Type_Code eru.DocTypeCode `json:"type-code"`
Type_Code ere.DocTypeCode `json:"type-code"`
Upload_Employee_Id *string `json:"encounter-document-employee-id"`
}
@@ -52,7 +52,7 @@ type MetaDto struct {
type ResponseDto struct {
ecore.Main
Encounter_Id *uint `json:"encounter_id"`
Type_Code eru.DocTypeCode `json:"type_code"`
Type_Code ere.DocTypeCode `json:"type_code"`
Name string `json:"name"`
FilePath *string `json:"filePath"`
FileName *string `json:"fileName"`
@@ -3,13 +3,13 @@ package encounter_document
import (
ecore "simrs-vx/internal/domain/base-entities/core"
ee "simrs-vx/internal/domain/main-entities/employee"
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
)
type EncounterDocument struct {
ecore.Main
Encounter_Id *uint `json:"encounter_id"`
Type_Code eru.DocTypeCode `json:"type_code"`
Type_Code ere.DocTypeCode `json:"type_code"`
Name string `json:"name"`
FilePath *string `json:"filePath"`
FileName *string `json:"fileName"`
@@ -0,0 +1,64 @@
package generalconsent
import (
ecore "simrs-vx/internal/domain/base-entities/core"
)
type CreateDto struct {
Encounter_Id *uint `json:"encounter_id"`
Value *string `json:"value"`
}
type ReadListDto struct {
FilterDto
Includes string `json:"includes"`
Pagination ecore.Pagination
}
type FilterDto struct {
Encounter_Id *uint `json:"encounter-id"`
}
type ReadDetailDto struct {
Id uint `json:"id"`
}
type UpdateDto struct {
Id uint `json:"id"`
CreateDto
}
type DeleteDto struct {
Id uint `json:"id"`
}
type MetaDto struct {
PageNumber int `json:"page_number"`
PageSize int `json:"page_size"`
Count int `json:"count"`
}
type ResponseDto struct {
ecore.Main
Encounter_Id *uint `json:"encounter_id"`
Value *string `json:"value"`
FileUrl *string `json:"fileUrl"`
}
func (d GeneralConsent) ToResponse() ResponseDto {
resp := ResponseDto{
Encounter_Id: d.Encounter_Id,
Value: d.Value,
FileUrl: d.FileUrl,
}
resp.Main = d.Main
return resp
}
func ToResponseList(data []GeneralConsent) []ResponseDto {
resp := make([]ResponseDto, len(data))
for i, u := range data {
resp[i] = u.ToResponse()
}
return resp
}
@@ -1,4 +1,4 @@
package general_consent
package generalconsent
import (
"simrs-vx/internal/domain/base-entities/core"
+2 -2
View File
@@ -12,7 +12,7 @@ import (
epr "simrs-vx/internal/domain/main-entities/person-relative"
erc "simrs-vx/internal/domain/references/common"
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
pa "simrs-vx/internal/lib/auth"
)
@@ -69,7 +69,7 @@ type SearchDto struct {
type UploadDto struct {
Id uint `json:"-"`
Code eru.DocTypeCode `json:"-"`
Code ere.DocTypeCode `json:"-"`
File multipart.File `json:"-"`
FileHeader *multipart.FileHeader `json:"-"`
Filename string `json:"-"`
@@ -17,6 +17,7 @@ type (
CrudCode string
DataApprovalCode string
ProcessStatusCode string
DocFormatTypeCode string
)
const (
@@ -106,6 +107,11 @@ const (
PSCSuccess ProcessStatusCode = "success"
PSCFailed ProcessStatusCode = "failed"
DFTCPDF DocFormatTypeCode = "pdf"
DFTCTXLSX DocFormatTypeCode = "xlsx"
DFTCTCSV DocFormatTypeCode = "csv"
DFTCTXLS DocFormatTypeCode = "xls"
)
func GetDayCodes() map[DayCode]string {
@@ -1,5 +1,7 @@
package encounter
import "fmt"
type (
EncounterClassCode string
QueueStatusCode string
@@ -18,6 +20,8 @@ type (
SEPRefTypeCode string
VisitModeCode string
PolySwitchCode string
DocTypeCode string
EntityTypeCode string
)
const (
@@ -104,6 +108,22 @@ const (
PSCConsulPoly PolySwitchCode = "consul-poly" // Konsultasi Poliklinik Lain
PSCConsulExecutive PolySwitchCode = "consul-executive" // Konsultasi Antar Dokter Eksekutif
DTCPRN DocTypeCode = "person-resident-number" // Person Resident Number
DTCPDL DocTypeCode = "person-driver-license" // Person Driver License
DTCPP DocTypeCode = "person-passport" // Person Passport
DTCPFC DocTypeCode = "person-family-card" // Person Family Card
DTCMIR DocTypeCode = "mcu-item-result" // Mcu Item Result
DTCEnPatient DocTypeCode = "encounter-patient"
DTCEnSupport DocTypeCode = "encounter-support"
DTCEnOther DocTypeCode = "encounter-other"
DTCSEP DocTypeCode = "vclaim-sep" // SEP
DTCSIPP DocTypeCode = "vclaim-sipp" // SIPP
DTCGC DocTypeCode = "general-consent"
ETCPerson EntityTypeCode = "person"
ETCEncounter EntityTypeCode = "encounter"
ETCMCU EntityTypeCode = "mcu"
)
func (ec EncounterClassCode) Code() string {
@@ -118,3 +138,30 @@ func (ec EncounterClassCode) Code() string {
return "UNKNOWN"
}
}
var validUploadCodesByEntity = map[EntityTypeCode][]DocTypeCode{
ETCPerson: {
DTCPRN, DTCPDL, DTCPP, DTCPFC,
},
ETCEncounter: {
DTCSEP, DTCSIPP, DTCEnPatient, DTCEnSupport, DTCGC, DTCEnOther,
},
ETCMCU: {
DTCMIR,
},
}
func IsValidUploadCode(entity EntityTypeCode, code DocTypeCode) (bool, string) {
allowedCodes, ok := validUploadCodesByEntity[entity]
if !ok {
return false, fmt.Sprintf("unknown entityType_code: %s", entity)
}
for _, c := range allowedCodes {
if c == code {
return true, ""
}
}
return false, fmt.Sprintf("invalid doctype_code '%s' for entityType_code '%s'", code, entity)
}
@@ -1,53 +0,0 @@
package upload
import "fmt"
type (
DocTypeCode string
EntityTypeCode string
)
const (
DTCPRN DocTypeCode = "person-resident-number" // Person Resident Number
DTCPDL DocTypeCode = "person-driver-license" // Person Driver License
DTCPP DocTypeCode = "person-passport" // Person Passport
DTCPFC DocTypeCode = "person-family-card" // Person Family Card
DTCMIR DocTypeCode = "mcu-item-result" // Mcu Item Result
DTCEnPatient DocTypeCode = "encounter-patient"
DTCEnSupport DocTypeCode = "encounter-support"
DTCEnOther DocTypeCode = "encounter-other"
DTCSEP DocTypeCode = "vclaim-sep" // SEP
DTCSIPP DocTypeCode = "vclaim-sipp" // SIPP
DTCGC DocTypeCode = "general-consent"
ETCPerson EntityTypeCode = "person"
ETCEncounter EntityTypeCode = "encounter"
ETCMCU EntityTypeCode = "mcu"
)
var validUploadCodesByEntity = map[EntityTypeCode][]DocTypeCode{
ETCPerson: {
DTCPRN, DTCPDL, DTCPP, DTCPFC,
},
ETCEncounter: {
DTCSEP, DTCSIPP, DTCEnPatient, DTCEnSupport, DTCEnOther,
},
ETCMCU: {
DTCMIR,
},
}
func IsValidUploadCode(entity EntityTypeCode, code DocTypeCode) (bool, string) {
allowedCodes, ok := validUploadCodesByEntity[entity]
if !ok {
return false, fmt.Sprintf("unknown entityType_code: %s", entity)
}
for _, c := range allowedCodes {
if c == code {
return true, ""
}
}
return false, fmt.Sprintf("invalid doctype_code '%s' for entityType_code '%s'", code, entity)
}
+17
View File
@@ -0,0 +1,17 @@
package docscfg
import a "github.com/karincake/apem"
type DocsCfg struct {
Path string
}
var O DocsCfg = DocsCfg{}
func ParseCfg() {
a.ParseSingleCfg(&O)
}
func (c DocsCfg) GetPath() string {
return c.Path
}
@@ -0,0 +1,72 @@
package generalconsent
import (
"net/http"
rw "github.com/karincake/risoles"
sf "github.com/karincake/semprit"
// ua "github.com/karincake/tumpeng/auth/svc"
e "simrs-vx/internal/domain/main-entities/general-consent"
u "simrs-vx/internal/use-case/main-use-case/general-consent"
)
type myBase struct{}
var O myBase
func (obj myBase) Create(w http.ResponseWriter, r *http.Request) {
dto := e.CreateDto{}
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return
}
res, err := u.Create(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{}
sf.UrlQueryParam(&dto, *r.URL)
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"))
if id <= 0 {
return
}
dto := e.UpdateDto{}
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return
}
dto.Id = uint(id)
res, err := u.Update(dto)
rw.DataResponse(w, res, err)
}
func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) {
id := rw.ValidateInt(w, "id", r.PathValue("id"))
if id <= 0 {
return
}
dto := e.DeleteDto{}
dto.Id = uint(id)
res, err := u.Delete(dto)
rw.DataResponse(w, res, err)
}
@@ -0,0 +1,18 @@
package generatefile
import (
"net/http"
rw "github.com/karincake/risoles"
u "simrs-vx/internal/use-case/main-use-case/generate-file"
)
func Generate(w http.ResponseWriter, r *http.Request) {
dto := u.GenerateDto{}
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return
}
res, err := u.Generate(dto)
rw.DataResponse(w, res, err)
}
@@ -17,6 +17,8 @@ import (
deviceorderitem "simrs-vx/internal/interface/main-handler/device-order-item"
encounter "simrs-vx/internal/interface/main-handler/encounter"
encounterdocument "simrs-vx/internal/interface/main-handler/encounter-document"
generalconsent "simrs-vx/internal/interface/main-handler/general-consent"
generatefile "simrs-vx/internal/interface/main-handler/generate-file"
internalreference "simrs-vx/internal/interface/main-handler/internal-reference"
materialorder "simrs-vx/internal/interface/main-handler/material-order"
materialorderitem "simrs-vx/internal/interface/main-handler/material-order-item"
@@ -34,7 +36,7 @@ import (
responsibledoctorhist "simrs-vx/internal/interface/main-handler/responsible-doctor-hist"
sbar "simrs-vx/internal/interface/main-handler/sbar"
soapi "simrs-vx/internal/interface/main-handler/soapi"
upload "simrs-vx/internal/interface/main-handler/upload"
uploadfile "simrs-vx/internal/interface/main-handler/upload-file"
/******************** actor ********************/
authpartner "simrs-vx/internal/interface/main-handler/auth-partner"
@@ -57,6 +59,7 @@ import (
/******************** infra ********************/
ibpjs "simrs-vx/internal/infra/bpjs"
docscfg "simrs-vx/internal/infra/docs-cfg"
gs "simrs-vx/internal/infra/gorm-setting"
minio "simrs-vx/internal/infra/minio"
ssdb "simrs-vx/internal/infra/ss-db"
@@ -131,6 +134,7 @@ func SetRoutes() http.Handler {
a.RegisterExtCall(ibpjs.SetConfig)
a.RegisterExtCall(validation.RegisterValidation)
a.RegisterExtCall(simgossync.SetConfig)
a.RegisterExtCall(docscfg.ParseCfg)
r := http.NewServeMux()
@@ -274,8 +278,10 @@ func SetRoutes() http.Handler {
"PATCH /{id}/reject": therapyprotocol.O.Reject,
})
hc.RegCrud(r, "/v1/chemo-protocol", chemoprotocol.O)
hc.RegCrud(r, "/v1/upload", upload.O)
hc.RegCrud(r, "/v1/upload-file", uploadfile.O)
hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O)
hc.RegCrud(r, "/v1/general-consent", generalconsent.O)
r.HandleFunc("POST /v1/generate-file", generatefile.Generate)
/******************** actor ********************/
hc.RegCrud(r, "/v1/person", person.O)
@@ -1,4 +1,4 @@
package upload
package uploadfile
import (
"net/http"
@@ -7,10 +7,9 @@ import (
d "github.com/karincake/dodol"
rw "github.com/karincake/risoles"
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
e "simrs-vx/internal/domain/main-entities/upload"
u "simrs-vx/internal/use-case/main-use-case/upload"
u "simrs-vx/internal/use-case/main-use-case/upload-file"
)
type myBase struct{}
@@ -49,10 +48,10 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) {
}
}
dto := e.CreateDto{
EntityType_Code: eru.EntityTypeCode(r.FormValue("entityType_code")),
dto := u.CreateDto{
EntityType_Code: ere.EntityTypeCode(r.FormValue("entityType_code")),
Ref_Id: refID,
Type_Code: eru.DocTypeCode(r.FormValue("type_code")),
Type_Code: ere.DocTypeCode(r.FormValue("type_code")),
Name: r.FormValue("name"),
Upload_Employee_Id: employeeId,
File: file,
@@ -1,10 +1,10 @@
package upload
package uploadfile
import (
e "simrs-vx/internal/domain/main-entities/upload"
u "simrs-vx/internal/use-case/main-use-case/upload-file"
)
func validateCreate(dto e.CreateDto) string {
func validateCreate(dto u.CreateDto) string {
switch {
case dto.EntityType_Code == "":
@@ -10,7 +10,7 @@ import (
d "github.com/karincake/dodol"
"gorm.io/gorm"
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
e "simrs-vx/internal/domain/main-entities/encounter-document"
)
@@ -244,7 +244,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) {
return err
}
if err := removeUploadedFile(string(eru.ETCEncounter), *data.FilePath, &event); err != nil {
if err := removeUploadedFile(string(ere.ETCEncounter), *data.FilePath, &event); err != nil {
return err
}
@@ -0,0 +1,295 @@
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"
dg "github.com/karincake/apem/db-gorm-pg"
d "github.com/karincake/dodol"
"gorm.io/gorm"
)
const source = "general-consent"
func Create(input e.CreateDto) (*d.Data, error) {
data := e.GeneralConsent{}
event := pl.Event{
Feature: "Create",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "create")
err := dg.I.Transaction(func(tx *gorm.DB) error {
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil {
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 {
data = *resData
}
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil {
return err
}
pl.SetLogInfo(&event, nil, "complete")
return nil
})
if err != nil {
return nil, err
}
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 data *e.GeneralConsent
var dataList []e.GeneralConsent
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 {
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil {
return err
}
if dataList, metaList, err = ReadListData(input, &event, tx); err != nil {
return err
}
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); 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.GeneralConsent
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 {
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil {
return err
}
if data, err = ReadDetailData(input, &event, tx); err != nil {
return err
}
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); 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.GeneralConsent
var err error
event := pl.Event{
Feature: "Update",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "update")
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
}
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil {
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
}
// Get Updated Data
if data, err = ReadDetailData(rdDto, &event, tx); err != nil {
return err
}
pl.SetLogInfo(&event, nil, "complete")
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
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: uint(input.Id)}
var data *e.GeneralConsent
var err error
event := pl.Event{
Feature: "Delete",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "delete")
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
}
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil {
return err
}
if err := DeleteData(data, &event, tx); err != nil {
return err
}
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return &d.Data{
Meta: d.IS{
"source": source,
"structure": "single-data",
"status": "deleted",
},
Data: data.ToResponse(),
}, nil
}
@@ -0,0 +1,22 @@
/*
DESCRIPTION:
Any functions that are used internally by the use-case
*/
package generalconsent
import (
e "simrs-vx/internal/domain/main-entities/general-consent"
)
func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.GeneralConsent) {
var inputSrc *e.CreateDto
if inputT, ok := any(input).(*e.CreateDto); ok {
inputSrc = inputT
} else {
inputTemp := any(input).(*e.UpdateDto)
inputSrc = &inputTemp.CreateDto
}
data.Encounter_Id = inputSrc.Encounter_Id
data.Value = inputSrc.Value
}
@@ -0,0 +1,140 @@
package generalconsent
import (
"errors"
e "simrs-vx/internal/domain/main-entities/general-consent"
plh "simrs-vx/pkg/lib-helper"
pl "simrs-vx/pkg/logger"
pu "simrs-vx/pkg/use-case-helper"
dg "github.com/karincake/apem/db-gorm-pg"
gh "github.com/karincake/getuk"
"gorm.io/gorm"
)
func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.GeneralConsent, error) {
pl.SetLogInfo(event, nil, "started", "DBCreate")
data := e.GeneralConsent{}
setData(&input, &data)
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
if err := tx.Create(&data).Error; err != nil {
return nil, plh.HandleCreateError(input, event, err)
}
pl.SetLogInfo(event, nil, "complete")
return &data, nil
}
func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.GeneralConsent, *e.MetaDto, error) {
pl.SetLogInfo(event, input, "started", "DBReadList")
data := []e.GeneralConsent{}
pagination := gh.Pagination{}
count := int64(0)
meta := e.MetaDto{}
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
tx = tx.
Model(&e.GeneralConsent{}).
Scopes(gh.Preload(input.Includes)).
Scopes(gh.Filter(input.FilterDto)).
Count(&count).
Scopes(gh.Paginate(input, &pagination)).
Order("\"CreatedAt\" DESC")
if err := tx.Find(&data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, &meta, nil
}
return nil, nil, plh.HandleListError(input, event, err)
}
meta.Count = int(count)
meta.PageNumber = pagination.PageNumber
meta.PageSize = pagination.PageSize
pl.SetLogInfo(event, nil, "complete")
return data, &meta, nil
}
func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.GeneralConsent, error) {
pl.SetLogInfo(event, input, "started", "DBReadDetail")
data := e.GeneralConsent{}
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
if err := tx.First(&data, input.Id).Error; err != nil {
if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil {
return nil, processedErr
}
}
pl.SetLogInfo(event, nil, "complete")
return &data, nil
}
func UpdateData(input e.UpdateDto, data *e.GeneralConsent, event *pl.Event, dbx ...*gorm.DB) error {
pl.SetLogInfo(event, data, "started", "DBUpdate")
setData(&input, data)
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
if err := tx.Save(&data).Error; err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-update-fail",
Detail: "Database update failed",
Raw: err,
}
return pl.SetLogError(event, input)
}
pl.SetLogInfo(event, nil, "complete")
return nil
}
func DeleteData(data *e.GeneralConsent, event *pl.Event, dbx ...*gorm.DB) error {
pl.SetLogInfo(event, data, "started", "DBDelete")
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
if err := tx.Delete(&data).Error; err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-delete-fail",
Detail: "Database delete failed",
Raw: err,
}
return pl.SetLogError(event, data)
}
pl.SetLogInfo(event, nil, "complete")
return nil
}
@@ -1,12 +1,11 @@
package upload
package generalconsent
import (
e "simrs-vx/internal/domain/main-entities/general-consent"
pl "simrs-vx/pkg/logger"
pu "simrs-vx/pkg/use-case-helper"
"gorm.io/gorm"
e "simrs-vx/internal/domain/main-entities/upload"
)
type middlewareRunner struct {
@@ -24,7 +23,7 @@ func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner {
}
// ExecuteCreateMiddleware executes create middleware
func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data interface{}) error {
func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.GeneralConsent) error {
for _, middleware := range middlewares {
logData := pu.GetLogData(input, data)
@@ -39,7 +38,7 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e
return nil
}
func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data interface{}) error {
func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.GeneralConsent) error {
for _, middleware := range middlewares {
logData := pu.GetLogData(input, data)
@@ -54,7 +53,7 @@ func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, inpu
return nil
}
func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error {
func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) error {
for _, middleware := range middlewares {
logData := pu.GetLogData(input, data)
@@ -69,7 +68,7 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw,
return nil
}
func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error {
func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) error {
for _, middleware := range middlewares {
logData := pu.GetLogData(input, data)
@@ -84,7 +83,7 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu
return nil
}
func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data interface{}) error {
func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.GeneralConsent) error {
for _, middleware := range middlewares {
logData := pu.GetLogData(input, data)
@@ -1,4 +1,4 @@
package upload
package generalconsent
// example of middleware
// func init() {
@@ -6,27 +6,27 @@ In this sample it also provides type and variable regarding the needs of the
middleware to separate from main use-case which has the basic CRUD
functionality. The purpose of this is to make the code more maintainable.
*/
package upload
package generalconsent
import (
"gorm.io/gorm"
e "simrs-vx/internal/domain/main-entities/upload"
e "simrs-vx/internal/domain/main-entities/general-consent"
)
type createMw struct {
Name string
Func func(input *e.CreateDto, data interface{}, tx *gorm.DB) error
Func func(input *e.CreateDto, data *e.GeneralConsent, tx *gorm.DB) error
}
type readListMw struct {
Name string
Func func(input *e.ReadListDto, data interface{}, tx *gorm.DB) error
Func func(input *e.ReadListDto, data *e.GeneralConsent, tx *gorm.DB) error
}
type readDetailMw struct {
Name string
Func func(input *e.ReadDetailDto, data interface{}, tx *gorm.DB) error
Func func(input *e.ReadDetailDto, data *e.GeneralConsent, tx *gorm.DB) error
}
type UpdateMw = readDetailMw
@@ -0,0 +1,94 @@
package generatefile
import (
"encoding/json"
"errors"
egc "simrs-vx/internal/domain/main-entities/general-consent"
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"
erc "simrs-vx/internal/domain/references/common"
ere "simrs-vx/internal/domain/references/encounter"
)
const source = "generate-file"
func Generate(input GenerateDto) (*d.Data, error) {
event := pl.Event{
Feature: "Generate",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "create")
switch input.Type_Code {
// general-consent
case ere.DTCGC:
// get value from general consent by ref_id
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 {
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 := dg.I.Save(&gc).Error; err != nil {
return nil, err
}
response := ResponseDto{
FileUrl: urlPub,
}
return &d.Data{
Meta: d.II{
"source": source,
"structure": "single-data",
"status": "created",
},
Data: response,
}, nil
default:
return nil, errors.New("invalid type code")
}
}
@@ -0,0 +1,94 @@
package generatefile
import (
"errors"
"fmt"
"mime"
"path/filepath"
"time"
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("./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,
}); 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)
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
}
@@ -0,0 +1,76 @@
package uploadfile
import (
"errors"
pl "simrs-vx/pkg/logger"
dg "github.com/karincake/apem/db-gorm-pg"
d "github.com/karincake/dodol"
"gorm.io/gorm"
ere "simrs-vx/internal/domain/references/encounter"
)
const source = "upload-file"
func Upload(input CreateDto) (*d.Data, error) {
event := pl.Event{
Feature: "Upload",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "create")
var (
err error
)
err = dg.I.Transaction(func(tx *gorm.DB) error {
// validate entityType_Code and Type_Code
valid, msg := ere.IsValidUploadCode(input.EntityType_Code, input.Type_Code)
if !valid {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "invalid-code",
Detail: msg,
Raw: errors.New(msg),
}
return pl.SetLogError(&event, input)
}
// upload file
input.FilePath, err = uploadAndGenerateFileUrl(input, &event)
if err != nil {
return err
}
if input.EntityType_Code == ere.ETCEncounter {
_, err = setEncounterDocument(input, &event, tx)
if err != nil {
return err
}
} else if input.EntityType_Code == ere.ETCPerson {
_, err = setPersonAttachment(input, &event, tx)
}
pl.SetLogInfo(&event, nil, "complete")
return nil
})
if err != nil {
return nil, err
}
return &d.Data{
Meta: d.II{
"source": source,
"structure": "single-data",
"status": "created",
},
Data: d.IS{
"fileUrl": input.FilePath,
},
}, nil
}
@@ -2,13 +2,13 @@
DESCRIPTION:
Any functions that are used internally by the use-case
*/
package upload
package uploadfile
import (
"fmt"
"path/filepath"
ecore "simrs-vx/internal/domain/base-entities/core"
eru "simrs-vx/internal/domain/references/upload"
ere "simrs-vx/internal/domain/references/encounter"
"strings"
"time"
@@ -21,13 +21,12 @@ import (
ee "simrs-vx/internal/domain/main-entities/encounter-document"
ep "simrs-vx/internal/domain/main-entities/person"
e "simrs-vx/internal/domain/main-entities/upload"
ue "simrs-vx/internal/use-case/main-use-case/encounter-document"
up "simrs-vx/internal/use-case/main-use-case/person"
)
func uploadAndGenerateFileUrl(input e.CreateDto, event *pl.Event) (string, error) {
func uploadAndGenerateFileUrl(input CreateDto, event *pl.Event) (string, error) {
pl.SetLogInfo(event, input, "started", "uploadAndGenerateFileUrl")
bucket := string(input.EntityType_Code)
ext := strings.ToLower(filepath.Ext(input.Filename))
@@ -97,7 +96,7 @@ func updatePersonAttachment(dataPerson *ep.Person, event *pl.Event, dbx ...*gorm
return nil
}
func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.Person, error) {
func setPersonAttachment(input CreateDto, event *pl.Event, tx *gorm.DB) (*ep.Person, error) {
// get person
dataPerson, err := up.ReadDetailData(ep.ReadDetailDto{Id: *input.Ref_Id}, event, tx)
if err != nil {
@@ -106,22 +105,22 @@ func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.P
var removeUrl string
switch input.Type_Code {
case eru.DTCPRN:
case ere.DTCPRN:
if dataPerson.ResidentIdentityFileUrl != nil {
removeUrl = *dataPerson.ResidentIdentityFileUrl
}
dataPerson.ResidentIdentityFileUrl = &input.FilePath
case eru.DTCPDL:
case ere.DTCPDL:
if dataPerson.DrivingLicenseFileUrl != nil {
removeUrl = *dataPerson.DrivingLicenseFileUrl
}
dataPerson.DrivingLicenseFileUrl = &input.FilePath
case eru.DTCPP:
case ere.DTCPP:
if dataPerson.PassportFileUrl != nil {
removeUrl = *dataPerson.PassportFileUrl
}
dataPerson.PassportFileUrl = &input.FilePath
case eru.DTCPFC:
case ere.DTCPFC:
if dataPerson.FamilyIdentityFileUrl != nil {
removeUrl = *dataPerson.FamilyIdentityFileUrl
}
@@ -141,7 +140,7 @@ func setPersonAttachment(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ep.P
return dataPerson, nil
}
func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee.EncounterDocument, error) {
func setEncounterDocument(input CreateDto, event *pl.Event, tx *gorm.DB) (*ee.EncounterDocument, error) {
data := ee.EncounterDocument{}
// get EncounterDocument
@@ -164,7 +163,7 @@ func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee.
Upload_Employee_Id: input.Upload_Employee_Id,
}
if input.Type_Code == eru.DTCSEP || input.Type_Code == eru.DTCSIPP {
if input.Type_Code == ere.DTCSEP || input.Type_Code == ere.DTCSIPP {
if len(dataUpload) > 0 {
data = dataUpload[0]
@@ -1,17 +1,22 @@
package upload
/*
DESCRIPTION:
A sample, part of the package that contains type, constants, and/or variables.
In this sample it also provides type and variable regarding the needs of the
middleware to separate from main use-case which has the basic CRUD
functionality. The purpose of this is to make the code more maintainable.
*/
package uploadfile
import (
"mime/multipart"
eru "simrs-vx/internal/domain/references/upload"
// internal - domain - base-entities
ecore "simrs-vx/internal/domain/base-entities/core"
ere "simrs-vx/internal/domain/references/encounter"
)
type CreateDto struct {
EntityType_Code eru.EntityTypeCode `form:"entityType_code"`
EntityType_Code ere.EntityTypeCode `form:"entityType_code"`
Ref_Id *uint `form:"ref_id"`
Type_Code eru.DocTypeCode `form:"type_code"`
Type_Code ere.DocTypeCode `form:"type_code"`
Name string `form:"name"`
Upload_Employee_Id *uint `form:"upload_employee_id"`
FilePath string `json:"-"`
@@ -23,46 +28,10 @@ type CreateDto struct {
MimeType string `json:"-"`
}
type ReadListDto struct {
FilterDto
Includes string `json:"includes"`
Pagination ecore.Pagination
}
type FilterDto struct {
EntityType_Code eru.EntityTypeCode `json:"entityType-code"`
Ref_Id *uint `json:"ref-id"`
Type_Code eru.DocTypeCode `json:"type-code"`
Name string `json:"name"`
FilePath *string `json:"filePath"`
FileName *string `json:"fileName"`
Upload_Employee_Id *string `json:"encounter-document-employee-id"`
}
type ReadDetailDto struct {
Id uint16 `json:"id"`
Includes string `json:"includes"`
}
type UpdateDto struct {
Id uint16 `json:"id"`
CreateDto
}
type DeleteDto struct {
Id uint16 `json:"id"`
}
type MetaDto struct {
PageNumber int `json:"page_number"`
PageSize int `json:"page_size"`
Count int `json:"count"`
}
type ResponseDto struct {
EntityType_Code eru.EntityTypeCode `json:"entityType_code"`
EntityType_Code ere.EntityTypeCode `json:"entityType_code"`
Ref_Id *uint `json:"ref_id"`
Type_Code eru.DocTypeCode `json:"type_code"`
Type_Code ere.DocTypeCode `json:"type_code"`
Name string `json:"name"`
Upload_Employee_Id *uint `json:"upload_employee_id"`
FilePath string `json:"filePath"`
@@ -1,90 +0,0 @@
package upload
import (
"errors"
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"
"gorm.io/gorm"
eru "simrs-vx/internal/domain/references/upload"
e "simrs-vx/internal/domain/main-entities/upload"
)
const source = "upload"
func Upload(input e.CreateDto) (*d.Data, error) {
event := pl.Event{
Feature: "Upload",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "create")
var data interface{}
err := dg.I.Transaction(func(tx *gorm.DB) error {
mwRunner := newMiddlewareRunner(&event, tx)
mwRunner.setMwType(pu.MWTPre)
// Run pre-middleware
err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data)
if err != nil {
return err
}
// validate entityType_Code and Type_Code
valid, msg := eru.IsValidUploadCode(input.EntityType_Code, input.Type_Code)
if !valid {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "invalid-code",
Detail: msg,
Raw: errors.New(msg),
}
return pl.SetLogError(&event, input)
}
// upload file
input.FilePath, err = uploadAndGenerateFileUrl(input, &event)
if err != nil {
return err
}
if input.EntityType_Code == eru.ETCEncounter {
data, err = setEncounterDocument(input, &event, tx)
if err != nil {
return err
}
} else if input.EntityType_Code == eru.ETCPerson {
data, err = setPersonAttachment(input, &event, tx)
}
mwRunner.setMwType(pu.MWTPost)
// Run post-middleware
if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil {
return err
}
pl.SetLogInfo(&event, nil, "complete")
return nil
})
if err != nil {
return nil, err
}
return &d.Data{
Meta: d.II{
"source": source,
"structure": "single-data",
"status": "created",
},
Data: input.ToResponse(),
}, nil
}