From ebc2ca5659e2665f8509c8419dab49d516601435 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 13 Nov 2025 09:57:10 +0700 Subject: [PATCH 01/20] set infra --- cmd/main-sync-api/config.yml-example | 41 --- cmd/main-sync-api/main.go | 15 - cmd/simgos-sync-api/config.yml-example | 6 +- cmd/simgos-sync-migration/Makefile | 18 ++ cmd/simgos-sync-migration/README-ATLAS.MD | 59 ++++ cmd/simgos-sync-migration/atlas.hcl.example | 22 ++ cmd/simgos-sync-migration/migration.go | 9 + internal/infra/sync-cfg/sync-cfg.go | 8 +- internal/infra/sync-cfg/tycovar.go | 5 +- .../interface/main-handler/main-handler.go | 2 + .../main-sync-handler/main-sync-handler.go | 36 --- internal/interface/migration/migration.go | 2 + .../migration/simgos-sync-entities.go | 5 + internal/interface/migration/tycovar.go | 5 +- .../installation/handler.go | 51 ++++ .../simgos-sync-handler.go | 9 +- internal/use-case/main-sync-use-case/.keep | 0 .../simgos-sync-use-case/installation/case.go | 263 ++++++++++++++++++ .../installation/helper.go | 23 ++ .../simgos-sync-use-case/installation/lib.go | 149 ++++++++++ .../installation/middleware-runner.go | 103 +++++++ .../installation/middleware.go | 9 + .../installation/tycovar.go | 44 +++ .../handler-crud-helper.go | 64 +++++ 24 files changed, 843 insertions(+), 105 deletions(-) delete mode 100644 cmd/main-sync-api/config.yml-example delete mode 100644 cmd/main-sync-api/main.go create mode 100644 cmd/simgos-sync-migration/Makefile create mode 100644 cmd/simgos-sync-migration/README-ATLAS.MD create mode 100644 cmd/simgos-sync-migration/atlas.hcl.example create mode 100644 cmd/simgos-sync-migration/migration.go delete mode 100644 internal/interface/main-sync-handler/main-sync-handler.go create mode 100644 internal/interface/migration/simgos-sync-entities.go create mode 100644 internal/interface/simgos-sync-handler/installation/handler.go delete mode 100644 internal/use-case/main-sync-use-case/.keep create mode 100644 internal/use-case/simgos-sync-use-case/installation/case.go create mode 100644 internal/use-case/simgos-sync-use-case/installation/helper.go create mode 100644 internal/use-case/simgos-sync-use-case/installation/lib.go create mode 100644 internal/use-case/simgos-sync-use-case/installation/middleware-runner.go create mode 100644 internal/use-case/simgos-sync-use-case/installation/middleware.go create mode 100644 internal/use-case/simgos-sync-use-case/installation/tycovar.go diff --git a/cmd/main-sync-api/config.yml-example b/cmd/main-sync-api/config.yml-example deleted file mode 100644 index edb5c84b..00000000 --- a/cmd/main-sync-api/config.yml-example +++ /dev/null @@ -1,41 +0,0 @@ -appCfg: - fullName: SIMRS Sync - codeName: simrs-sync - version: 0.1.0 - env: development - lang: en - -dbCfg: - dsn: - maxOpenConns: 5 - maxIdleConns: 5 - maxIdleTime: 100 - -multiDbCfg: - dbs : - - name: simrs_sync - dsn: - maxOpenConns: 5 - maxIdleConns: 5 - maxIdleTime: 100 - -httpCfg: - host: 127.0.0.1 - port: 8003 - -loggerCfg: - hideTime: true - hideLevel: true - -langCfg: - active: en - path: ../../assets/language/en - fileName: data.json - -corsCfg: - allowedOrigin: - allowedMethod: GET, POST, PUT, PATCH, DELETE, OPTIONS - -syncUrlCfg: - target: - host: \ No newline at end of file diff --git a/cmd/main-sync-api/main.go b/cmd/main-sync-api/main.go deleted file mode 100644 index 5bfdd36b..00000000 --- a/cmd/main-sync-api/main.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - a "github.com/karincake/apem" - - h "simrs-vx/internal/interface/main-sync-handler" - - d "github.com/karincake/apem/db-gorm-pg" - - l "github.com/karincake/apem/logger-zerolog" -) - -func main() { - a.Run(h.SetRoutes(), &l.O, &d.O) -} diff --git a/cmd/simgos-sync-api/config.yml-example b/cmd/simgos-sync-api/config.yml-example index 153be50e..98544958 100644 --- a/cmd/simgos-sync-api/config.yml-example +++ b/cmd/simgos-sync-api/config.yml-example @@ -34,8 +34,4 @@ langCfg: corsCfg: allowedOrigin: - allowedMethod: GET, POST, PUT, PATCH, DELETE, OPTIONS - -syncUrlCfg: - target: - host: \ No newline at end of file + allowedMethod: GET, POST, PUT, PATCH, DELETE, OPTIONS \ No newline at end of file diff --git a/cmd/simgos-sync-migration/Makefile b/cmd/simgos-sync-migration/Makefile new file mode 100644 index 00000000..e83356e5 --- /dev/null +++ b/cmd/simgos-sync-migration/Makefile @@ -0,0 +1,18 @@ +# Makefile for Atlas migrations + +# Default environment +ENV ?= gorm + +.PHONY: diff apply hash + +## Generate a new migration diff +diff: + atlas migrate diff --env $(ENV) + +## Apply migrations to the database +apply: + atlas migrate apply --env $(ENV) + +## Calculate the schema hash +hash: + atlas migrate hash diff --git a/cmd/simgos-sync-migration/README-ATLAS.MD b/cmd/simgos-sync-migration/README-ATLAS.MD new file mode 100644 index 00000000..da249823 --- /dev/null +++ b/cmd/simgos-sync-migration/README-ATLAS.MD @@ -0,0 +1,59 @@ +# Database Migration with Atlas + +This project uses [Atlas](https://atlasgo.io/) for database schema management and migrations. + +## 📋 Prerequisites + +1. **Download and Install Atlas CLI** + Run the following command in PowerShell or Git Bash: + + ```sh + curl -sSf https://atlasgo.sh | sh + ``` + Verify installation: + + ```sh + atlas version + ``` + +2. Install GORM Provider + Run inside your Go project: + + ```sh + go get -u ariga.io/atlas-provider-gorm + ``` + +3. Create atlas.hcl configuration file + Just create an atlas.hcl file in your project root as example given at atlas.hcl.example +4. Create migrations folder + ```sh + mkdir migrations + ``` +5. Usage +You can use the provided Makefile for common commands: + + Generate a migration diff + ```sh + make diff + ``` + + Apply migrations + ```sh + make apply + ``` + + Compute schema hash + ```sh + make hash + ``` + + If you don’t have make installed, you can run the Atlas commands directly: + ```sh + atlas migrate diff --env gorm + ``` + ```sh + atlas migrate apply --env gorm + ``` + ```sh + atlas migrate hash + ``` \ No newline at end of file diff --git a/cmd/simgos-sync-migration/atlas.hcl.example b/cmd/simgos-sync-migration/atlas.hcl.example new file mode 100644 index 00000000..857d1352 --- /dev/null +++ b/cmd/simgos-sync-migration/atlas.hcl.example @@ -0,0 +1,22 @@ +data "external_schema" "gorm" { + program = [ + "go", + "run", + "-mod=mod", + ".", + ] +} + +env "gorm" { + src = data.external_schema.gorm.url + dev = "" // dsn db to check the diff + migration { + dir = "file://migrations" + } + url = "" // dsn db to apply + format { + migrate { + diff = "{{ sql . \" \" }}" + } + } +} \ No newline at end of file diff --git a/cmd/simgos-sync-migration/migration.go b/cmd/simgos-sync-migration/migration.go new file mode 100644 index 00000000..fdd2c995 --- /dev/null +++ b/cmd/simgos-sync-migration/migration.go @@ -0,0 +1,9 @@ +package main + +import ( + m "simrs-vx/internal/interface/migration" +) + +func main() { + m.Migrate(m.SimgosSync) +} diff --git a/internal/infra/sync-cfg/sync-cfg.go b/internal/infra/sync-cfg/sync-cfg.go index 0368a7c4..a5579219 100644 --- a/internal/infra/sync-cfg/sync-cfg.go +++ b/internal/infra/sync-cfg/sync-cfg.go @@ -7,8 +7,12 @@ import ( func SetConfig() { a.ParseSingleCfg(&O) - if O.Host == "" || O.Target == "" { - panic("sync url config host or target empty") + //if O.Host == "" || O.Target == "" { + // panic("sync url config host or target empty") + //} + + if O.BaseUrl == "" { + panic("config bpjs base url empty") } lo.I.Println("sync url config loaded, status: DONE!!") } diff --git a/internal/infra/sync-cfg/tycovar.go b/internal/infra/sync-cfg/tycovar.go index 9bb59f3d..081ef762 100644 --- a/internal/infra/sync-cfg/tycovar.go +++ b/internal/infra/sync-cfg/tycovar.go @@ -3,6 +3,7 @@ package synccfg var O SyncUrlCfg = SyncUrlCfg{} type SyncUrlCfg struct { - Target string `yaml:"target"` - Host string `yaml:"host"` + Target string `yaml:"target"` + Host string `yaml:"host"` + BaseUrl string `yaml:"baseUrl"` } diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 8e885dc3..55f45479 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -60,6 +60,7 @@ import ( gs "simrs-vx/internal/infra/gorm-setting" minio "simrs-vx/internal/infra/minio" ssdb "simrs-vx/internal/infra/ss-db" + simgossync "simrs-vx/internal/infra/sync-cfg" /******************** pkg ********************/ cmw "simrs-vx/pkg/cors-manager-mw" @@ -128,6 +129,7 @@ func SetRoutes() http.Handler { a.RegisterExtCall(mh.I.SetClient) a.RegisterExtCall(ibpjs.SetConfig) a.RegisterExtCall(validation.RegisterValidation) + a.RegisterExtCall(simgossync.SetConfig) r := http.NewServeMux() diff --git a/internal/interface/main-sync-handler/main-sync-handler.go b/internal/interface/main-sync-handler/main-sync-handler.go deleted file mode 100644 index c8ba5144..00000000 --- a/internal/interface/main-sync-handler/main-sync-handler.go +++ /dev/null @@ -1,36 +0,0 @@ -package mainsynchandler - -import ( - "net/http" - - /******************** infra ********************/ - gs "simrs-vx/internal/infra/gorm-setting" - synccfg "simrs-vx/internal/infra/sync-cfg" - - /******************** pkg ********************/ - cmw "simrs-vx/pkg/cors-manager-mw" - lh "simrs-vx/pkg/lang-helper" - handlerlogger "simrs-vx/pkg/middleware/handler-logger" - zlc "simrs-vx/pkg/zerolog-ctx" - - /******************** external ********************/ - a "github.com/karincake/apem" - - /******************** internal ********************/ - "simrs-vx/internal/interface/main-handler/home" -) - -func SetRoutes() http.Handler { - /// - a.RegisterExtCall(gs.Adjust) - a.RegisterExtCall(zlc.Adjust) - a.RegisterExtCall(lh.Populate) - a.RegisterExtCall(synccfg.SetConfig) - - r := http.NewServeMux() - - /******************** Main ********************/ - r.HandleFunc("/", home.Home) - - return cmw.SetCors(handlerlogger.SetLog(r)) -} diff --git a/internal/interface/migration/migration.go b/internal/interface/migration/migration.go index 6cec68dc..a446ac1a 100644 --- a/internal/interface/migration/migration.go +++ b/internal/interface/migration/migration.go @@ -31,6 +31,8 @@ func getEntities(input string) []any { return getMainEntities() case "satusehat": return getSatuSehatEntities() + case "simgos-sync": + return getSimgosSync() } return nil } diff --git a/internal/interface/migration/simgos-sync-entities.go b/internal/interface/migration/simgos-sync-entities.go new file mode 100644 index 00000000..c6b51988 --- /dev/null +++ b/internal/interface/migration/simgos-sync-entities.go @@ -0,0 +1,5 @@ +package migration + +func getSimgosSync() []any { + return []any{} +} diff --git a/internal/interface/migration/tycovar.go b/internal/interface/migration/tycovar.go index f86f855d..977c4689 100644 --- a/internal/interface/migration/tycovar.go +++ b/internal/interface/migration/tycovar.go @@ -2,6 +2,7 @@ package migration const ( - Main = "main" - SatuSehat = "satusehat" + Main = "main" + SatuSehat = "satusehat" + SimgosSync = "simgos-sync" ) diff --git a/internal/interface/simgos-sync-handler/installation/handler.go b/internal/interface/simgos-sync-handler/installation/handler.go new file mode 100644 index 00000000..8e8b15bf --- /dev/null +++ b/internal/interface/simgos-sync-handler/installation/handler.go @@ -0,0 +1,51 @@ +package installation + +import ( + "net/http" + + rw "github.com/karincake/risoles" + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/installation" + u "simrs-vx/internal/use-case/main-use-case/installation" +) + +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) Update(w http.ResponseWriter, r *http.Request) { + code := rw.ValidateString(w, "code", r.PathValue("code")) + if code == "" { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Code = &code + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + code := rw.ValidateString(w, "code", r.PathValue("code")) + if code == "" { + return + } + + dto := e.DeleteDto{} + dto.Code = &code + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index f51106a9..ed3246f7 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -2,10 +2,10 @@ package simgossynchandler import ( "net/http" + hc "simrs-vx/pkg/handler-crud-helper" /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" - synccfg "simrs-vx/internal/infra/sync-cfg" /******************** pkg ********************/ cmw "simrs-vx/pkg/cors-manager-mw" @@ -18,6 +18,9 @@ import ( /******************** internal ********************/ "simrs-vx/internal/interface/main-handler/home" + + /********************* source *********************/ + installation "simrs-vx/internal/interface/simgos-sync-handler/installation" ) func SetRoutes() http.Handler { @@ -25,12 +28,14 @@ func SetRoutes() http.Handler { a.RegisterExtCall(gs.Adjust) a.RegisterExtCall(zlc.Adjust) a.RegisterExtCall(lh.Populate) - a.RegisterExtCall(synccfg.SetConfig) r := http.NewServeMux() /******************** Main ********************/ r.HandleFunc("/", home.Home) + /******************** Source ******************/ + hc.RegCrudByCode(r, "/v1/installation", installation.O) + return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/use-case/main-sync-use-case/.keep b/internal/use-case/main-sync-use-case/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go new file mode 100644 index 00000000..7d8187cc --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -0,0 +1,263 @@ +package installation + +import ( + e "simrs-vx/internal/domain/main-entities/installation" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "installation" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Installation{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + 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.Installation + var dataList []e.Installation + 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.Installation + 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{Code: input.Code} + var data *e.Installation + 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 + } + + if err := UpdateData(input, data, &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{Code: input.Code} + var data *e.Installation + 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 + +} diff --git a/internal/use-case/simgos-sync-use-case/installation/helper.go b/internal/use-case/simgos-sync-use-case/installation/helper.go new file mode 100644 index 00000000..d3d96b5c --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/helper.go @@ -0,0 +1,23 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package installation + +import ( + e "simrs-vx/internal/domain/main-entities/installation" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Installation) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Code = *inputSrc.Code + data.Name = inputSrc.Name + data.EncounterClass_Code = inputSrc.EncounterClass_Code +} diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go new file mode 100644 index 00000000..d75b2ecd --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -0,0 +1,149 @@ +package installation + +import ( + e "simrs-vx/internal/domain/main-entities/installation" + + 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.Installation, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.Installation{} + 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.Installation, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.Installation{} + 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.Installation{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if 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.Installation, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.Installation{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if input.Code != nil { + tx = tx.Where("\"Code\" = ?", *input.Code) + } + if input.Id != nil { + tx = tx.Where("\"Id\" = ?", *input.Id) + } + + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data).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.Installation, 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.Installation, 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 +} diff --git a/internal/use-case/simgos-sync-use-case/installation/middleware-runner.go b/internal/use-case/simgos-sync-use-case/installation/middleware-runner.go new file mode 100644 index 00000000..fce572bb --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/middleware-runner.go @@ -0,0 +1,103 @@ +package installation + +import ( + e "simrs-vx/internal/domain/main-entities/installation" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Installation) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Installation) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/simgos-sync-use-case/installation/middleware.go b/internal/use-case/simgos-sync-use-case/installation/middleware.go new file mode 100644 index 00000000..3d414c9b --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/middleware.go @@ -0,0 +1,9 @@ +package installation + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/simgos-sync-use-case/installation/tycovar.go b/internal/use-case/simgos-sync-use-case/installation/tycovar.go new file mode 100644 index 00000000..de8e9c9e --- /dev/null +++ b/internal/use-case/simgos-sync-use-case/installation/tycovar.go @@ -0,0 +1,44 @@ +/* +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 installation + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Installation, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Installation, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Installation, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/pkg/handler-crud-helper/handler-crud-helper.go b/pkg/handler-crud-helper/handler-crud-helper.go index df475094..a5700dad 100644 --- a/pkg/handler-crud-helper/handler-crud-helper.go +++ b/pkg/handler-crud-helper/handler-crud-helper.go @@ -74,3 +74,67 @@ func RegCrudByCode(r *http.ServeMux, path string, mwAndRouter ...any) { "DELETE /{code}": c.Delete, }) } + +func RegCrudSimgosSync(r *http.ServeMux, path string, mwAndRouter ...any) { + sLength := len(mwAndRouter) + + mwCandidates := mwAndRouter[:sLength-1] + mwList := []hk.HandlerMw{} + for i := range mwCandidates { + // have to do it manually, since casting directly results unexpected result + myType := reflect.TypeOf(mwCandidates[i]) + if myType.String() != "func(http.Handler) http.Handler" { + panic("non middleware included as middleware") + } + mwList = append(mwList, mwCandidates[i].(func(http.Handler) http.Handler)) + + // if g, okHandler := mwCandidates[i].(func(http.Handler) http.Handler); !okHandler { + // panic("non middleware included") + // } else { + // mwList = append(mwList, g) + // } + } + + c, ok := mwAndRouter[sLength-1].(CrudBase) + if !ok { + panic("non CrudBase used in the last paramter") + } + + hk.GroupRoutes(path, r, mwList, hk.MapHandlerFunc{ + "POST /": c.Create, + "PATCH /{id}": c.Update, + "DELETE /{id}": c.Delete, + }) +} + +func RegCrudByCodeSimgosSync(r *http.ServeMux, path string, mwAndRouter ...any) { + sLength := len(mwAndRouter) + + mwCandidates := mwAndRouter[:sLength-1] + mwList := []hk.HandlerMw{} + for i := range mwCandidates { + // have to do it manually, since casting directly results unexpected result + myType := reflect.TypeOf(mwCandidates[i]) + if myType.String() != "func(http.Handler) http.Handler" { + panic("non middleware included as middleware") + } + mwList = append(mwList, mwCandidates[i].(func(http.Handler) http.Handler)) + + // if g, okHandler := mwCandidates[i].(func(http.Handler) http.Handler); !okHandler { + // panic("non middleware included") + // } else { + // mwList = append(mwList, g) + // } + } + + c, ok := mwAndRouter[sLength-1].(CrudBase) + if !ok { + panic("non CrudBase used in the last paramter") + } + + hk.GroupRoutes(path, r, mwList, hk.MapHandlerFunc{ + "POST /": c.Create, + "PATCH /{code}": c.Update, + "DELETE /{code}": c.Delete, + }) +} From 91afde0624c73680444965abc6b707d8bd2f0ebd Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 13 Nov 2025 11:39:12 +0700 Subject: [PATCH 02/20] on going installation --- .../installation/handler.go | 2 +- .../simgos-sync-handler.go | 11 ++-- .../main-use-case/installation/case.go | 3 +- .../simgos-sync-use-case/installation/case.go | 4 ++ .../installation/helper.go | 5 +- .../simgos-sync-use-case/installation/lib.go | 9 +-- .../handler-crud-helper.go | 64 ------------------- 7 files changed, 22 insertions(+), 76 deletions(-) diff --git a/internal/interface/simgos-sync-handler/installation/handler.go b/internal/interface/simgos-sync-handler/installation/handler.go index 8e8b15bf..01a6beab 100644 --- a/internal/interface/simgos-sync-handler/installation/handler.go +++ b/internal/interface/simgos-sync-handler/installation/handler.go @@ -7,7 +7,7 @@ import ( // ua "github.com/karincake/tumpeng/auth/svc" e "simrs-vx/internal/domain/main-entities/installation" - u "simrs-vx/internal/use-case/main-use-case/installation" + u "simrs-vx/internal/use-case/simgos-sync-use-case/installation" ) type myBase struct{} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index ed3246f7..963ec93c 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -2,8 +2,6 @@ package simgossynchandler import ( "net/http" - hc "simrs-vx/pkg/handler-crud-helper" - /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" @@ -15,11 +13,10 @@ import ( /******************** external ********************/ a "github.com/karincake/apem" + hk "github.com/karincake/hongkue" /******************** internal ********************/ "simrs-vx/internal/interface/main-handler/home" - - /********************* source *********************/ installation "simrs-vx/internal/interface/simgos-sync-handler/installation" ) @@ -35,7 +32,11 @@ func SetRoutes() http.Handler { r.HandleFunc("/", home.Home) /******************** Source ******************/ - hc.RegCrudByCode(r, "/v1/installation", installation.O) + hk.GroupRoutes("/v1/installation", r, hk.MapHandlerFunc{ + "POST /": installation.O.Create, + "PATCH /{id}": installation.O.Update, + "DELETE /{id}": installation.O.Delete, + }) return cmw.SetCors(handlerlogger.SetLog(r)) } diff --git a/internal/use-case/main-use-case/installation/case.go b/internal/use-case/main-use-case/installation/case.go index f66bedaf..f3ce5d59 100644 --- a/internal/use-case/main-use-case/installation/case.go +++ b/internal/use-case/main-use-case/installation/case.go @@ -1,7 +1,6 @@ package installation import ( - e "simrs-vx/internal/domain/main-entities/installation" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -11,6 +10,8 @@ import ( pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" ) const source = "installation" diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index 7d8187cc..398289cd 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -27,6 +27,10 @@ func Create(input e.CreateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "create") err := dg.I.Transaction(func(tx *gorm.DB) error { + // TODO: Insert + + // TODO: InsertSimxLog + if resData, err := CreateData(input, &event, tx); err != nil { return err } else { diff --git a/internal/use-case/simgos-sync-use-case/installation/helper.go b/internal/use-case/simgos-sync-use-case/installation/helper.go index d3d96b5c..af56b5c6 100644 --- a/internal/use-case/simgos-sync-use-case/installation/helper.go +++ b/internal/use-case/simgos-sync-use-case/installation/helper.go @@ -6,9 +6,10 @@ package installation import ( e "simrs-vx/internal/domain/main-entities/installation" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" ) -func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Installation) { +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T, data *esimgos.MInstalasi) { var inputSrc *e.CreateDto if inputT, ok := any(input).(*e.CreateDto); ok { inputSrc = inputT @@ -17,6 +18,8 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Installation) { inputSrc = &inputTemp.CreateDto } + data.NamaInstalasi = inputSrc.Name + data.StatusRawatInap = 1 data.Code = *inputSrc.Code data.Name = inputSrc.Name data.EncounterClass_Code = inputSrc.EncounterClass_Code diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index d75b2ecd..07afe61a 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -1,8 +1,6 @@ package installation import ( - e "simrs-vx/internal/domain/main-entities/installation" - plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -10,12 +8,15 @@ import ( dg "github.com/karincake/apem/db-gorm-pg" gh "github.com/karincake/getuk" "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" ) -func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Installation, error) { +func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MInstalasi, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") - data := e.Installation{} + data := esimgos.MInstalasi{} setData(&input, &data) var tx *gorm.DB diff --git a/pkg/handler-crud-helper/handler-crud-helper.go b/pkg/handler-crud-helper/handler-crud-helper.go index a5700dad..df475094 100644 --- a/pkg/handler-crud-helper/handler-crud-helper.go +++ b/pkg/handler-crud-helper/handler-crud-helper.go @@ -74,67 +74,3 @@ func RegCrudByCode(r *http.ServeMux, path string, mwAndRouter ...any) { "DELETE /{code}": c.Delete, }) } - -func RegCrudSimgosSync(r *http.ServeMux, path string, mwAndRouter ...any) { - sLength := len(mwAndRouter) - - mwCandidates := mwAndRouter[:sLength-1] - mwList := []hk.HandlerMw{} - for i := range mwCandidates { - // have to do it manually, since casting directly results unexpected result - myType := reflect.TypeOf(mwCandidates[i]) - if myType.String() != "func(http.Handler) http.Handler" { - panic("non middleware included as middleware") - } - mwList = append(mwList, mwCandidates[i].(func(http.Handler) http.Handler)) - - // if g, okHandler := mwCandidates[i].(func(http.Handler) http.Handler); !okHandler { - // panic("non middleware included") - // } else { - // mwList = append(mwList, g) - // } - } - - c, ok := mwAndRouter[sLength-1].(CrudBase) - if !ok { - panic("non CrudBase used in the last paramter") - } - - hk.GroupRoutes(path, r, mwList, hk.MapHandlerFunc{ - "POST /": c.Create, - "PATCH /{id}": c.Update, - "DELETE /{id}": c.Delete, - }) -} - -func RegCrudByCodeSimgosSync(r *http.ServeMux, path string, mwAndRouter ...any) { - sLength := len(mwAndRouter) - - mwCandidates := mwAndRouter[:sLength-1] - mwList := []hk.HandlerMw{} - for i := range mwCandidates { - // have to do it manually, since casting directly results unexpected result - myType := reflect.TypeOf(mwCandidates[i]) - if myType.String() != "func(http.Handler) http.Handler" { - panic("non middleware included as middleware") - } - mwList = append(mwList, mwCandidates[i].(func(http.Handler) http.Handler)) - - // if g, okHandler := mwCandidates[i].(func(http.Handler) http.Handler); !okHandler { - // panic("non middleware included") - // } else { - // mwList = append(mwList, g) - // } - } - - c, ok := mwAndRouter[sLength-1].(CrudBase) - if !ok { - panic("non CrudBase used in the last paramter") - } - - hk.GroupRoutes(path, r, mwList, hk.MapHandlerFunc{ - "POST /": c.Create, - "PATCH /{code}": c.Update, - "DELETE /{code}": c.Delete, - }) -} From 1e54d3bc3f68110b717e0d45d961c157b0fc31f5 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Thu, 13 Nov 2025 14:51:45 +0700 Subject: [PATCH 03/20] fix (encounter): add guard for create, delete and cancel --- .../domain/main-entities/encounter/dto.go | 4 ++ .../main-handler/encounter/handler.go | 11 +++++ internal/lib/auth/tycovar.go | 2 +- .../main-use-case/authentication/case.go | 2 +- .../use-case/main-use-case/encounter/case.go | 48 +++++++++++++++---- pkg/use-case-helper/use-case-helper.go | 16 +++++++ 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index d3f2d04d..f43a1de9 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -104,10 +104,14 @@ type UpdateDto struct { type UpdateStatusDto struct { Id uint16 `json:"id"` StatusCode erc.DataStatusCode `json:"status_code"` + + pa.AuthInfo } type DeleteDto struct { Id uint16 `json:"id"` + + pa.AuthInfo } type MetaDto struct { diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 22d1d8cf..c8fcfa1a 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -89,9 +89,14 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { if id <= 0 { return } + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } dto := e.DeleteDto{} dto.Id = uint16(id) + dto.AuthInfo = *authInfo res, err := u.Delete(dto) rw.DataResponse(w, res, err) } @@ -159,11 +164,17 @@ func (obj myBase) Cancel(w http.ResponseWriter, r *http.Request) { return } + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } + dto := e.UpdateStatusDto{ Id: uint16(id), StatusCode: erc.DSCCancel, } + dto.AuthInfo = *authInfo res, err := u.UpdateStatusCode(dto) rw.DataResponse(w, res, err) } diff --git a/internal/lib/auth/tycovar.go b/internal/lib/auth/tycovar.go index 467bd1b6..3567cd61 100644 --- a/internal/lib/auth/tycovar.go +++ b/internal/lib/auth/tycovar.go @@ -11,7 +11,7 @@ type AuthInfo struct { Uuid string User_Id uint User_Name string - User_ContractPosition_code string + User_ContractPosition_Code string Employee_Position_Code *string Employee_Id *uint Doctor_Code *string diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index 15f18ef8..db83d315 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -205,7 +205,7 @@ func ExtractToken(r *http.Request, tokenType TokenType) (data *pa.AuthInfo, err User_Name: fmt.Sprintf("%v", claims["user_name"]), } - data.User_ContractPosition_code = checkStrClaims(claims, "contractPosition_code") + data.User_ContractPosition_Code = checkStrClaims(claims, "contractPosition_code") data.Employee_Position_Code = checkStrPtrClaims(claims, "employee_position_code") data.Doctor_Code = checkStrPtrClaims(claims, "doctor_code") data.Nurse_Code = checkStrPtrClaims(claims, "nurse_code") diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 9381e639..8a006a35 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -16,10 +16,10 @@ import ( erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" + erg "simrs-vx/internal/domain/references/organization" eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" edc "simrs-vx/internal/domain/main-entities/death-cause" - eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" eir "simrs-vx/internal/domain/main-entities/internal-reference" erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" @@ -27,7 +27,6 @@ import ( uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" udc "simrs-vx/internal/use-case/main-use-case/death-cause" - uem "simrs-vx/internal/use-case/main-use-case/employee" uir "simrs-vx/internal/use-case/main-use-case/internal-reference" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" ) @@ -79,6 +78,19 @@ func Create(input e.CreateDto) (*d.Data, error) { } } + // check only user with registration position is allowed to create encounter + if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration position is allowed to create encounter", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } else { + input.Adm_Employee_Id = input.AuthInfo.Employee_Id + } + // check if patient is new in the hospital input.NewStatus, err = identifyPatientStatus(input) @@ -90,12 +102,6 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - if emp, err := uem.ReadDetailData(eem.ReadDetailDto{User_Id: &input.AuthInfo.User_Id}, &event, tx); err != nil { - return err - } else { - input.Adm_Employee_Id = &emp.Id - } - // create encounter if resData, err := CreateData(input, &event, tx); err != nil { return err @@ -322,6 +328,17 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + // check only user with registration position is allowed to create encounter + if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration position is allowed to create encounter", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } + err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { @@ -494,6 +511,21 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { // TODO: Prevent cancellation if the billing has been verified // TODO: Only "supervisi pendaftaran" could cancel encounter + roleAllowedToCancel := []string{ + string(erg.EPCReg), + string(erg.EPCNur), + string(erg.EPCDoc), + } + + if !pu.Contains(roleAllowedToCancel, input.AuthInfo.User_ContractPosition_Code) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration, nurse, or doctor position is allowed to cancel encounter", + Raw: errors.New("authentication failed"), + } + return pl.SetLogError(&event, input) + } // Prevent cancellation if soapi exist encounterId := uint(input.Id) diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go index a80595bb..07df05ba 100644 --- a/pkg/use-case-helper/use-case-helper.go +++ b/pkg/use-case-helper/use-case-helper.go @@ -150,3 +150,19 @@ func IsDateBeforeNow(t *time.Time) bool { } return t.Before(time.Now()) } + +// Contains reports whether v is present in s. +func Contains[S ~[]E, E comparable](s S, v E) bool { + return index(s, v) >= 0 +} + +// Index returns the index of the first occurrence of v in s, +// or -1 if not present. +func index[S ~[]E, E comparable](s S, v E) int { + for i := range s { + if v == s[i] { + return i + } + } + return -1 +} From e75025f87d202e515d88ecd5d866e4852878b3db Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 13 Nov 2025 17:28:20 +0700 Subject: [PATCH 04/20] add create installation --- .../domain/main-entities/installation/dto.go | 1 + .../main-use-case/installation/case.go | 9 +- .../installation/middleware-runner.go | 3 +- .../main-use-case/installation/middleware.go | 15 +- .../main-use-case/installation/tycovar.go | 4 +- .../simgos-sync-plugin/installation/plugin.go | 46 +++++ .../simgos-sync-use-case/installation/case.go | 157 ++++++++++-------- .../installation/helper.go | 39 ++++- .../simgos-sync-use-case/installation/lib.go | 90 +++++++--- 9 files changed, 254 insertions(+), 110 deletions(-) create mode 100644 internal/use-case/simgos-sync-plugin/installation/plugin.go diff --git a/internal/domain/main-entities/installation/dto.go b/internal/domain/main-entities/installation/dto.go index 758efb05..5cc0ed5c 100644 --- a/internal/domain/main-entities/installation/dto.go +++ b/internal/domain/main-entities/installation/dto.go @@ -8,6 +8,7 @@ import ( ) type CreateDto struct { + Id *uint `json:"id"` Code *string `json:"code" validate:"maxLength=10"` Name string `json:"name" validate:"maxLength=50"` EncounterClass_Code ere.EncounterClassCode `json:"encounterClass_code" validate:"maxLength=10"` diff --git a/internal/use-case/main-use-case/installation/case.go b/internal/use-case/main-use-case/installation/case.go index f3ce5d59..8e577a30 100644 --- a/internal/use-case/main-use-case/installation/case.go +++ b/internal/use-case/main-use-case/installation/case.go @@ -1,6 +1,7 @@ package installation import ( + egos "simrs-vx/internal/domain/simgos-entities/installation" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -18,6 +19,7 @@ const source = "installation" func Create(input e.CreateDto) (*d.Data, error) { data := e.Installation{} + sgData := egos.MInstalasi{} event := pl.Event{ Feature: "Create", @@ -29,11 +31,6 @@ func Create(input e.CreateDto) (*d.Data, error) { 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 - } if resData, err := CreateData(input, &event, tx); err != nil { return err @@ -43,7 +40,7 @@ func Create(input e.CreateDto) (*d.Data, error) { mwRunner.setMwType(pu.MWTPost) // Run post-middleware - if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &sgData); err != nil { return err } diff --git a/internal/use-case/main-use-case/installation/middleware-runner.go b/internal/use-case/main-use-case/installation/middleware-runner.go index fce572bb..0abd6b29 100644 --- a/internal/use-case/main-use-case/installation/middleware-runner.go +++ b/internal/use-case/main-use-case/installation/middleware-runner.go @@ -2,6 +2,7 @@ package installation import ( e "simrs-vx/internal/domain/main-entities/installation" + egos "simrs-vx/internal/domain/simgos-entities/installation" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -23,7 +24,7 @@ func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Installation) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *egos.MInstalasi) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) diff --git a/internal/use-case/main-use-case/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index 3d414c9b..46c4be1d 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -1,9 +1,12 @@ package installation +import ( + plugin "simrs-vx/internal/use-case/simgos-sync-plugin/installation" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPostMw = append(createPostMw, + createMw{Name: "create-installation", Func: plugin.Create}, + ) +} diff --git a/internal/use-case/main-use-case/installation/tycovar.go b/internal/use-case/main-use-case/installation/tycovar.go index de8e9c9e..975ebd34 100644 --- a/internal/use-case/main-use-case/installation/tycovar.go +++ b/internal/use-case/main-use-case/installation/tycovar.go @@ -9,6 +9,8 @@ functionality. The purpose of this is to make the code more maintainable. package installation import ( + egos "simrs-vx/internal/domain/simgos-entities/installation" + "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" @@ -16,7 +18,7 @@ import ( type createMw struct { Name string - Func func(input *e.CreateDto, data *e.Installation, tx *gorm.DB) error + Func func(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error } type readListMw struct { diff --git a/internal/use-case/simgos-sync-plugin/installation/plugin.go b/internal/use-case/simgos-sync-plugin/installation/plugin.go new file mode 100644 index 00000000..92ef479b --- /dev/null +++ b/internal/use-case/simgos-sync-plugin/installation/plugin.go @@ -0,0 +1,46 @@ +package installation + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + sync "simrs-vx/internal/infra/sync-cfg" + + e "simrs-vx/internal/domain/main-entities/installation" + egos "simrs-vx/internal/domain/simgos-entities/installation" + + "gorm.io/gorm" +) + +var endpoint = fmt.Sprintf("%s/v1/installation", sync.O.BaseUrl) + +func Create(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error { + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if err := json.Unmarshal(body, &data); err != nil { + return fmt.Errorf("failed to parse response JSON: %w", err) + } + + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index 398289cd..2990d5ae 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,8 +1,9 @@ package installation import ( - e "simrs-vx/internal/domain/main-entities/installation" + erc "simrs-vx/internal/domain/references/common" "strconv" + "time" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -11,13 +12,18 @@ import ( pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" + egos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/installation" ) const source = "installation" -func Create(input e.CreateDto) (*d.Data, error) { - data := e.Installation{} +var now = time.Now() +func Create(input e.CreateDto) (*d.Data, error) { + sgData := egos.MInstalasi{} event := pl.Event{ Feature: "Create", Source: source, @@ -27,14 +33,35 @@ func Create(input e.CreateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "create") err := dg.I.Transaction(func(tx *gorm.DB) error { - // TODO: Insert + // Insert Simgos + sgData, err := CreateSimgosData(input, &event, tx) + if err != nil { + errMsg := err.Error() + inputLog := esync.InstallationSimxLog{ + Date: &now, + Status: erc.PSCFailed, + ErrMessage: &errMsg, + } - // TODO: InsertSimxLog - - if resData, err := CreateData(input, &event, tx); err != nil { - return err + // InsertSimxLog + if err = CreateSimxLogData(input, inputLog, &event, tx); err != nil { + return err + } } else { - data = *resData + inputLog := esync.InstallationSimxLog{ + Date: &now, + Status: erc.PSCSuccess, + } + + // InsertSimxLog + if err = CreateSimxLogData(input, inputLog, &event, tx); err != nil { + return err + } + } + + // InsertSimxLink + if err = CreateInstallationLinkData(*input.Id, sgData.NoInstalasi, &event, tx); err != nil { + return err } pl.SetLogInfo(&event, nil, "complete") @@ -52,7 +79,7 @@ func Create(input e.CreateDto) (*d.Data, error) { "structure": "single-data", "status": "created", }, - Data: data.ToResponse(), + Data: sgData, }, nil } @@ -156,61 +183,61 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { }, nil } -func Update(input e.UpdateDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Code: input.Code} - var data *e.Installation - 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 - } - - if err := UpdateData(input, data, &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 Update(input e.UpdateDto) (*d.Data, error) { +// rdDto := e.ReadDetailDto{Code: input.Code} +// var data *e.Installation +// 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 +// } +// +// if err := UpdateData(input, data, &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{Code: input.Code} diff --git a/internal/use-case/simgos-sync-use-case/installation/helper.go b/internal/use-case/simgos-sync-use-case/installation/helper.go index af56b5c6..eae50f99 100644 --- a/internal/use-case/simgos-sync-use-case/installation/helper.go +++ b/internal/use-case/simgos-sync-use-case/installation/helper.go @@ -5,11 +5,13 @@ Any functions that are used internally by the use-case package installation import ( + "encoding/json" e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/installation" ) -func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T, data *esimgos.MInstalasi) { +func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MInstalasi) { var inputSrc *e.CreateDto if inputT, ok := any(input).(*e.CreateDto); ok { inputSrc = inputT @@ -19,8 +21,35 @@ func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T, data *esimgos.MInstal } data.NamaInstalasi = inputSrc.Name - data.StatusRawatInap = 1 - data.Code = *inputSrc.Code - data.Name = inputSrc.Name - data.EncounterClass_Code = inputSrc.EncounterClass_Code + data.StatusRawatInap = 0 + data.StAktif = 1 + + return +} + +func setDataSimxLog[T *e.CreateDto | *e.UpdateDto](input T, inputLog esync.InstallationSimxLog) (data esync.InstallationSimxLog) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + // encode to JSON + jsonData, _ := json.Marshal(inputSrc) + jsonString := string(jsonData) + + data.Value = &jsonString + data.Date = &now + data.Status = inputLog.Status + data.ErrMessage = inputLog.ErrMessage + + return +} + +func setDataSimxLink(simxId, simgosId uint) (data esync.InstallationLink) { + data.Simx_Id = simxId + data.Simgos_Id = simgosId + return } diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index 07afe61a..e76eb127 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -11,13 +11,13 @@ import ( e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/installation" ) func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MInstalasi, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") - data := esimgos.MInstalasi{} - setData(&input, &data) + data := setDataSimgos(&input) var tx *gorm.DB if len(dbx) > 0 { @@ -34,6 +34,44 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi return &data, nil } +func CreateSimxLogData(input e.CreateDto, inputLog esync.InstallationSimxLog, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input, inputLog) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func CreateInstallationLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLink(simxId, simgosId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(data, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Installation, *e.MetaDto, error) { pl.SetLogInfo(event, input, "started", "DBReadList") data := []e.Installation{} @@ -101,30 +139,30 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e return &data, nil } -func UpdateData(input e.UpdateDto, data *e.Installation, 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 UpdateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { +// pl.SetLogInfo(event, input, "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.Installation, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBDelete") From 29469d4c50864b442455d88489132a45258c63e8 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 13 Nov 2025 20:54:35 +0700 Subject: [PATCH 05/20] add installation --- internal/domain/sync-entities/log/dto.go | 12 ++ internal/infra/simgos-db/simgos-db.go | 9 + internal/infra/simgos-db/tycovar.go | 5 + internal/infra/sync-cfg/sync-cfg.go | 7 +- internal/infra/sync-cfg/tycovar.go | 5 +- .../installation/handler.go | 46 ++-- .../simgos-sync-handler.go | 8 +- .../main-use-case/installation/case.go | 43 +++- .../installation/middleware-runner.go | 25 ++- .../main-use-case/installation/middleware.go | 6 +- .../main-use-case/installation/tycovar.go | 11 +- .../simgos-sync-plugin/installation/plugin.go | 49 ++++- .../simgos-sync-use-case/installation/case.go | 197 ++++++++++-------- .../installation/helper.go | 33 +-- .../simgos-sync-use-case/installation/lib.go | 20 +- 15 files changed, 313 insertions(+), 163 deletions(-) create mode 100644 internal/domain/sync-entities/log/dto.go create mode 100644 internal/infra/simgos-db/simgos-db.go create mode 100644 internal/infra/simgos-db/tycovar.go diff --git a/internal/domain/sync-entities/log/dto.go b/internal/domain/sync-entities/log/dto.go new file mode 100644 index 00000000..32f5be6f --- /dev/null +++ b/internal/domain/sync-entities/log/dto.go @@ -0,0 +1,12 @@ +package log + +import ( + erc "simrs-vx/internal/domain/references/common" +) + +type SimxLogDto struct { + Payload any `json:"payload"` + IsSuccess bool `json:"isSuccess"` + ErrMessage *string `json:"errMessage"` + Method erc.CrudCode `json:"method"` +} diff --git a/internal/infra/simgos-db/simgos-db.go b/internal/infra/simgos-db/simgos-db.go new file mode 100644 index 00000000..4387ad7f --- /dev/null +++ b/internal/infra/simgos-db/simgos-db.go @@ -0,0 +1,9 @@ +package simgos_db + +import ( + dg "github.com/karincake/apem/db-gorm-pg" +) + +func SetInstance() { + I = dg.IS["simrs"] +} diff --git a/internal/infra/simgos-db/tycovar.go b/internal/infra/simgos-db/tycovar.go new file mode 100644 index 00000000..b9c9f2b3 --- /dev/null +++ b/internal/infra/simgos-db/tycovar.go @@ -0,0 +1,5 @@ +package simgos_db + +import "gorm.io/gorm" + +var I *gorm.DB diff --git a/internal/infra/sync-cfg/sync-cfg.go b/internal/infra/sync-cfg/sync-cfg.go index a5579219..a6a5d703 100644 --- a/internal/infra/sync-cfg/sync-cfg.go +++ b/internal/infra/sync-cfg/sync-cfg.go @@ -7,12 +7,9 @@ import ( func SetConfig() { a.ParseSingleCfg(&O) - //if O.Host == "" || O.Target == "" { - // panic("sync url config host or target empty") - //} - if O.BaseUrl == "" { - panic("config bpjs base url empty") + if O.Host == "" || O.Prefix == "" { + panic("config sync host and prefix empty") } lo.I.Println("sync url config loaded, status: DONE!!") } diff --git a/internal/infra/sync-cfg/tycovar.go b/internal/infra/sync-cfg/tycovar.go index 081ef762..fdcda00b 100644 --- a/internal/infra/sync-cfg/tycovar.go +++ b/internal/infra/sync-cfg/tycovar.go @@ -3,7 +3,6 @@ package synccfg var O SyncUrlCfg = SyncUrlCfg{} type SyncUrlCfg struct { - Target string `yaml:"target"` - Host string `yaml:"host"` - BaseUrl string `yaml:"baseUrl"` + Prefix string `yaml:"prefix"` + Host string `yaml:"host"` } diff --git a/internal/interface/simgos-sync-handler/installation/handler.go b/internal/interface/simgos-sync-handler/installation/handler.go index 01a6beab..6f9a708d 100644 --- a/internal/interface/simgos-sync-handler/installation/handler.go +++ b/internal/interface/simgos-sync-handler/installation/handler.go @@ -7,6 +7,7 @@ import ( // ua "github.com/karincake/tumpeng/auth/svc" e "simrs-vx/internal/domain/main-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/log" u "simrs-vx/internal/use-case/simgos-sync-use-case/installation" ) @@ -23,29 +24,38 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { rw.DataResponse(w, res, err) } -func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { - code := rw.ValidateString(w, "code", r.PathValue("code")) - if code == "" { - return - } - - dto := e.UpdateDto{} +func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { + dto := esync.SimxLogDto{} if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { return } - dto.Code = &code - res, err := u.Update(dto) + res, err := u.CreateSimxLog(dto) rw.DataResponse(w, res, err) } +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + //code := rw.ValidateString(w, "code", r.PathValue("code")) + //if code == "" { + // return + //} + // + //dto := e.UpdateDto{} + //if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + // return + //} + //dto.Code = &code + //res, err := u.Update(dto) + //rw.DataResponse(w, res, err) +} + func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { - code := rw.ValidateString(w, "code", r.PathValue("code")) - if code == "" { - return - } - - dto := e.DeleteDto{} - dto.Code = &code - res, err := u.Delete(dto) - rw.DataResponse(w, res, err) + //code := rw.ValidateString(w, "code", r.PathValue("code")) + //if code == "" { + // return + //} + // + //dto := e.DeleteDto{} + //dto.Code = &code + //res, err := u.Delete(dto) + //rw.DataResponse(w, res, err) } diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index 963ec93c..71002949 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -2,8 +2,11 @@ package simgossynchandler import ( "net/http" + /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" + simgosdb "simrs-vx/internal/infra/simgos-db" + simgossync "simrs-vx/internal/infra/sync-cfg" /******************** pkg ********************/ cmw "simrs-vx/pkg/cors-manager-mw" @@ -25,6 +28,8 @@ func SetRoutes() http.Handler { a.RegisterExtCall(gs.Adjust) a.RegisterExtCall(zlc.Adjust) a.RegisterExtCall(lh.Populate) + a.RegisterExtCall(simgossync.SetConfig) + a.RegisterExtCall(simgosdb.SetInstance) r := http.NewServeMux() @@ -32,8 +37,9 @@ func SetRoutes() http.Handler { r.HandleFunc("/", home.Home) /******************** Source ******************/ - hk.GroupRoutes("/v1/installation", r, hk.MapHandlerFunc{ + hk.GroupRoutes("/new-to-old/v1/installation", r, hk.MapHandlerFunc{ "POST /": installation.O.Create, + "POST /log": installation.O.CreateLog, "PATCH /{id}": installation.O.Update, "DELETE /{id}": installation.O.Delete, }) diff --git a/internal/use-case/main-use-case/installation/case.go b/internal/use-case/main-use-case/installation/case.go index 8e577a30..d0a409c0 100644 --- a/internal/use-case/main-use-case/installation/case.go +++ b/internal/use-case/main-use-case/installation/case.go @@ -1,7 +1,7 @@ package installation import ( - egos "simrs-vx/internal/domain/simgos-entities/installation" + erc "simrs-vx/internal/domain/references/common" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -13,6 +13,8 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" + egos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/log" ) const source = "installation" @@ -28,19 +30,18 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + mwRunner := newMiddlewareRunner(&event) err := dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) - 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, &sgData); err != nil { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &sgData); err != nil { return err } @@ -49,7 +50,29 @@ func Create(input e.CreateDto) (*d.Data, error) { return nil }) + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return nil, err + } + + return nil, err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { return nil, err } @@ -78,7 +101,7 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { @@ -129,7 +152,7 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { @@ -182,7 +205,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { @@ -238,7 +261,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event, tx) + mwRunner := newMiddlewareRunner(&event) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { diff --git a/internal/use-case/main-use-case/installation/middleware-runner.go b/internal/use-case/main-use-case/installation/middleware-runner.go index 0abd6b29..acfaaa47 100644 --- a/internal/use-case/main-use-case/installation/middleware-runner.go +++ b/internal/use-case/main-use-case/installation/middleware-runner.go @@ -1,12 +1,14 @@ package installation import ( - e "simrs-vx/internal/domain/main-entities/installation" - egos "simrs-vx/internal/domain/simgos-entities/installation" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation" + egos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/log" ) type middlewareRunner struct { @@ -16,10 +18,9 @@ type middlewareRunner struct { } // NewMiddlewareExecutor creates a new middleware executor -func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { +func newMiddlewareRunner(event *pl.Event) *middlewareRunner { return &middlewareRunner{ Event: event, - Tx: tx, } } @@ -39,6 +40,22 @@ func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e return nil } +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateLogMiddleware(middlewares []createLogMw, input *esync.SimxLogDto) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, nil) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Installation) error { for _, middleware := range middlewares { logData := pu.GetLogData(input, data) diff --git a/internal/use-case/main-use-case/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index 46c4be1d..222ec563 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -6,7 +6,11 @@ import ( // example of middleware func init() { - createPostMw = append(createPostMw, + createPreMw = append(createPreMw, createMw{Name: "create-installation", Func: plugin.Create}, ) + + createSimxLogMw = append(createSimxLogMw, + createLogMw{Name: "create-log", Func: plugin.CreateLog}, + ) } diff --git a/internal/use-case/main-use-case/installation/tycovar.go b/internal/use-case/main-use-case/installation/tycovar.go index 975ebd34..e704b3a1 100644 --- a/internal/use-case/main-use-case/installation/tycovar.go +++ b/internal/use-case/main-use-case/installation/tycovar.go @@ -14,6 +14,7 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" + elog "simrs-vx/internal/domain/sync-entities/log" ) type createMw struct { @@ -21,6 +22,11 @@ type createMw struct { Func func(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error } +type createLogMw struct { + Name string + Func func(input *elog.SimxLogDto) error +} + type readListMw struct { Name string Func func(input *e.ReadListDto, data *e.Installation, tx *gorm.DB) error @@ -34,8 +40,9 @@ type readDetailMw struct { type UpdateMw = readDetailMw type DeleteMw = readDetailMw -var createPreMw []createMw // preprocess middleware -var createPostMw []createMw // postprocess middleware +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var createSimxLogMw []createLogMw var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw diff --git a/internal/use-case/simgos-sync-plugin/installation/plugin.go b/internal/use-case/simgos-sync-plugin/installation/plugin.go index 92ef479b..b566302b 100644 --- a/internal/use-case/simgos-sync-plugin/installation/plugin.go +++ b/internal/use-case/simgos-sync-plugin/installation/plugin.go @@ -6,25 +6,29 @@ import ( "fmt" "io" "net/http" + + "gorm.io/gorm" + sync "simrs-vx/internal/infra/sync-cfg" e "simrs-vx/internal/domain/main-entities/installation" egos "simrs-vx/internal/domain/simgos-entities/installation" - - "gorm.io/gorm" + elog "simrs-vx/internal/domain/sync-entities/log" ) -var endpoint = fmt.Sprintf("%s/v1/installation", sync.O.BaseUrl) - func Create(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error { + var endpoint = fmt.Sprintf("%s%s/v1/installation", sync.O.Host, sync.O.Prefix) + jsonData, err := json.Marshal(input) if err != nil { return fmt.Errorf("failed to encode JSON: %w", err) } + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) if err != nil { return err } + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) @@ -33,6 +37,10 @@ func Create(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error { } defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + body, err := io.ReadAll(resp.Body) if err != nil { return err @@ -44,3 +52,36 @@ func Create(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error { return nil } + +func CreateLog(input *elog.SimxLogDto) error { + var endpoint = fmt.Sprintf("%s%s/v1/installation/log", sync.O.Host, sync.O.Prefix) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index 2990d5ae..f38cca14 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,9 +1,8 @@ package installation import ( - erc "simrs-vx/internal/domain/references/common" + esync "simrs-vx/internal/domain/sync-entities/log" "strconv" - "time" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -14,16 +13,63 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" - egos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/installation" ) const source = "installation" -var now = time.Now() - func Create(input e.CreateDto) (*d.Data, error) { - sgData := egos.MInstalasi{} + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // Define trx + txSync := dg.I.Begin() + txSimgos := dg.IS["simrs"].Begin() + + // STEP 1: Insert to simgos + sgData, err := CreateSimgosData(input, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 2: Insert to Link + if err = CreateInstallationLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 3: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() + return nil, err + } + + if err = txSync.Commit().Error; err != nil { + // STEP 4: Rollback Partial + go func() { + _ = DeleteSimgosData(sgData, &event) + }() + return nil, err + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: sgData, + }, nil +} + +func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { event := pl.Event{ Feature: "Create", Source: source, @@ -33,34 +79,8 @@ func Create(input e.CreateDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "create") err := dg.I.Transaction(func(tx *gorm.DB) error { - // Insert Simgos - sgData, err := CreateSimgosData(input, &event, tx) - if err != nil { - errMsg := err.Error() - inputLog := esync.InstallationSimxLog{ - Date: &now, - Status: erc.PSCFailed, - ErrMessage: &errMsg, - } - - // InsertSimxLog - if err = CreateSimxLogData(input, inputLog, &event, tx); err != nil { - return err - } - } else { - inputLog := esync.InstallationSimxLog{ - Date: &now, - Status: erc.PSCSuccess, - } - - // InsertSimxLog - if err = CreateSimxLogData(input, inputLog, &event, tx); err != nil { - return err - } - } - - // InsertSimxLink - if err = CreateInstallationLinkData(*input.Id, sgData.NoInstalasi, &event, tx); err != nil { + // InsertSimxLog + if err := CreateSimxLogData(input, &event, tx); err != nil { return err } @@ -79,7 +99,6 @@ func Create(input e.CreateDto) (*d.Data, error) { "structure": "single-data", "status": "created", }, - Data: sgData, }, nil } @@ -239,56 +258,56 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { // //} -func Delete(input e.DeleteDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Code: input.Code} - var data *e.Installation - 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 - -} +//func Delete(input e.DeleteDto) (*d.Data, error) { +// rdDto := e.ReadDetailDto{Code: input.Code} +// var data *e.Installation +// 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 +// +//} diff --git a/internal/use-case/simgos-sync-use-case/installation/helper.go b/internal/use-case/simgos-sync-use-case/installation/helper.go index eae50f99..51316d79 100644 --- a/internal/use-case/simgos-sync-use-case/installation/helper.go +++ b/internal/use-case/simgos-sync-use-case/installation/helper.go @@ -7,8 +7,10 @@ package installation import ( "encoding/json" e "simrs-vx/internal/domain/main-entities/installation" + erc "simrs-vx/internal/domain/references/common" esimgos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/installation" + "simrs-vx/internal/domain/sync-entities/log" ) func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MInstalasi) { @@ -20,30 +22,31 @@ func setDataSimgos[T *e.CreateDto | *e.UpdateDto](input T) (data esimgos.MInstal inputSrc = &inputTemp.CreateDto } - data.NamaInstalasi = inputSrc.Name - data.StatusRawatInap = 0 - data.StAktif = 1 + data.Nama_Instalasi = inputSrc.Name + data.Status_Rawat_Inap = 0 + data.St_Aktif = 1 return } -func setDataSimxLog[T *e.CreateDto | *e.UpdateDto](input T, inputLog esync.InstallationSimxLog) (data esync.InstallationSimxLog) { - var inputSrc *e.CreateDto - if inputT, ok := any(input).(*e.CreateDto); ok { - inputSrc = inputT - } else { - inputTemp := any(input).(*e.UpdateDto) - inputSrc = &inputTemp.CreateDto - } - +func setDataSimxLog(input *log.SimxLogDto) (data esync.InstallationSimxLog) { // encode to JSON - jsonData, _ := json.Marshal(inputSrc) + jsonData, _ := json.MarshalIndent(input.Payload, "", " ") jsonString := string(jsonData) + var status erc.ProcessStatusCode + if input.IsSuccess { + status = erc.PSCSuccess + } else { + status = erc.PSCFailed + if input.ErrMessage != nil { + data.ErrMessage = input.ErrMessage + } + } + data.Value = &jsonString data.Date = &now - data.Status = inputLog.Status - data.ErrMessage = inputLog.ErrMessage + data.Status = status return } diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index e76eb127..b260776e 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -1,9 +1,11 @@ package installation import ( + esync "simrs-vx/internal/domain/sync-entities/log" plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" + "time" dg "github.com/karincake/apem/db-gorm-pg" gh "github.com/karincake/getuk" @@ -11,9 +13,10 @@ import ( e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" - esync "simrs-vx/internal/domain/sync-entities/installation" ) +var now = time.Now() + func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esimgos.MInstalasi, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") @@ -23,7 +26,7 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi if len(dbx) > 0 { tx = dbx[0] } else { - tx = dg.I + tx = dg.IS["simrs"] } if err := tx.Create(&data).Error; err != nil { @@ -34,9 +37,9 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi return &data, nil } -func CreateSimxLogData(input e.CreateDto, inputLog esync.InstallationSimxLog, event *pl.Event, dbx ...*gorm.DB) error { +func CreateSimxLogData(input esync.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "DBCreate") - data := setDataSimxLog(&input, inputLog) + data := setDataSimxLog(&input) var tx *gorm.DB if len(dbx) > 0 { @@ -164,14 +167,9 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e // return nil //} -func DeleteData(data *e.Installation, event *pl.Event, dbx ...*gorm.DB) error { +func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event) error { pl.SetLogInfo(event, data, "started", "DBDelete") - var tx *gorm.DB - if len(dbx) > 0 { - tx = dbx[0] - } else { - tx = dg.I - } + var tx = dg.IS["simrs"] if err := tx.Delete(&data).Error; err != nil { event.Status = "failed" From 73d869f4c94c003429ba20ad7e478556549ff8a9 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 09:52:07 +0700 Subject: [PATCH 06/20] feat (user): add position reg --- internal/use-case/main-use-case/user/case.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/use-case/main-use-case/user/case.go b/internal/use-case/main-use-case/user/case.go index 7c66b153..50b676af 100644 --- a/internal/use-case/main-use-case/user/case.go +++ b/internal/use-case/main-use-case/user/case.go @@ -166,6 +166,8 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := umw.CreateData(createMidWife, &event, tx); err != nil { return err } + case ero.EPCReg: + // do nothing default: return errors.New("invalid employee position") } From 9e4357780b25257f32558400f6fed15cb07f4ea9 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 14 Nov 2025 09:52:50 +0700 Subject: [PATCH 07/20] add sync installation --- .../installation/handler.go | 50 +-- .../main-use-case/installation/case.go | 126 +++---- .../installation/middleware-runner.go | 19 +- .../main-use-case/installation/middleware.go | 6 + .../main-use-case/installation/tycovar.go | 22 +- .../simgos-sync-plugin/installation/plugin.go | 150 +++++--- .../simgos-sync-use-case/installation/case.go | 324 +++++++----------- .../simgos-sync-use-case/installation/lib.go | 186 +++++----- 8 files changed, 442 insertions(+), 441 deletions(-) diff --git a/internal/interface/simgos-sync-handler/installation/handler.go b/internal/interface/simgos-sync-handler/installation/handler.go index 6f9a708d..36424e71 100644 --- a/internal/interface/simgos-sync-handler/installation/handler.go +++ b/internal/interface/simgos-sync-handler/installation/handler.go @@ -2,6 +2,7 @@ package installation import ( "net/http" + "strconv" rw "github.com/karincake/risoles" // ua "github.com/karincake/tumpeng/auth/svc" @@ -34,28 +35,35 @@ func (obj myBase) CreateLog(w http.ResponseWriter, r *http.Request) { } func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { - //code := rw.ValidateString(w, "code", r.PathValue("code")) - //if code == "" { - // return - //} - // - //dto := e.UpdateDto{} - //if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { - // return - //} - //dto.Code = &code - //res, err := u.Update(dto) - //rw.DataResponse(w, res, err) + id := rw.ValidateString(w, "id", r.PathValue("id")) + if id == "" { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + val, _ := strconv.ParseUint(id, 10, 16) + valUint := uint16(val) + dto.Id = &valUint + + res, err := u.Update(dto) + rw.DataResponse(w, res, err) } func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { - //code := rw.ValidateString(w, "code", r.PathValue("code")) - //if code == "" { - // return - //} - // - //dto := e.DeleteDto{} - //dto.Code = &code - //res, err := u.Delete(dto) - //rw.DataResponse(w, res, err) + id := rw.ValidateString(w, "id", r.PathValue("id")) + if id == "" { + return + } + + dto := e.DeleteDto{} + val, _ := strconv.ParseUint(id, 10, 16) + valUint := uint16(val) + dto.Id = &valUint + + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) } diff --git a/internal/use-case/main-use-case/installation/case.go b/internal/use-case/main-use-case/installation/case.go index d0a409c0..b80b6ca5 100644 --- a/internal/use-case/main-use-case/installation/case.go +++ b/internal/use-case/main-use-case/installation/case.go @@ -13,7 +13,6 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" - egos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/log" ) @@ -21,7 +20,6 @@ const source = "installation" func Create(input e.CreateDto) (*d.Data, error) { data := e.Installation{} - sgData := egos.MInstalasi{} event := pl.Event{ Feature: "Create", @@ -37,44 +35,24 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } else { data = *resData + id := uint(data.Id) + input.Id = &id } mwRunner.setMwType(pu.MWTPre) // Run pre-middleware - if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &sgData); err != nil { + if err := mwRunner.RunCreateMiddleware(createPreMw, &input); err != nil { return err } - pl.SetLogInfo(&event, nil, "complete") - return nil }) - var errMsg string - inputLog := esync.SimxLogDto{ - Payload: input, - Method: erc.CCCreate, - } - - if err != nil { - // Run log-middleware - errMsg = err.Error() - inputLog.ErrMessage = &errMsg - inputLog.IsSuccess = false - - // create log failed - if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { - return nil, err - } - + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } - // create log success - inputLog.IsSuccess = true - if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { - return nil, err - } + pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.II{ @@ -87,7 +65,6 @@ func Create(input e.CreateDto) (*d.Data, error) { } func ReadList(input e.ReadListDto) (*d.Data, error) { - var data *e.Installation var dataList []e.Installation var metaList *e.MetaDto var err error @@ -101,23 +78,11 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readList") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event) - 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 }) @@ -152,23 +117,11 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { pl.SetLogInfo(&event, input, "started", "readDetail") err = dg.I.Transaction(func(tx *gorm.DB) error { - mwRunner := newMiddlewareRunner(&event) - 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 }) @@ -198,6 +151,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "update") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -205,32 +159,26 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id if err := UpdateData(input, data, &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 { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(updatePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -254,6 +202,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + mwRunner := newMiddlewareRunner(&event) err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") @@ -261,30 +210,26 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner := newMiddlewareRunner(&event) - mwRunner.setMwType(pu.MWTPre) - // Run pre-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { - return err - } - + input.Id = &data.Id 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 { + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(deletePreMw, &input); err != nil { return err } return nil }) - if err != nil { + if err = runLogMiddleware(err, input, mwRunner); err != nil { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.IS{ "source": source, @@ -295,3 +240,32 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func runLogMiddleware(err error, input any, mwRunner *middlewareRunner) error { + var errMsg string + inputLog := esync.SimxLogDto{ + Payload: input, + Method: erc.CCCreate, + } + + if err != nil { + // Run log-middleware + errMsg = err.Error() + inputLog.ErrMessage = &errMsg + inputLog.IsSuccess = false + + // create log failed + if errMiddleware := mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); errMiddleware != nil { + return errMiddleware + } + return err + } + + // create log success + inputLog.IsSuccess = true + if err = mwRunner.RunCreateLogMiddleware(createSimxLogMw, &inputLog); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/installation/middleware-runner.go b/internal/use-case/main-use-case/installation/middleware-runner.go index acfaaa47..5a8e2ad0 100644 --- a/internal/use-case/main-use-case/installation/middleware-runner.go +++ b/internal/use-case/main-use-case/installation/middleware-runner.go @@ -7,7 +7,6 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" - egos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/log" ) @@ -25,13 +24,13 @@ func newMiddlewareRunner(event *pl.Event) *middlewareRunner { } // ExecuteCreateMiddleware executes create middleware -func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *egos.MInstalasi) error { +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -86,13 +85,13 @@ func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, return nil } -func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []UpdateMw, input *e.UpdateDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } @@ -101,13 +100,13 @@ func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, inpu return nil } -func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Installation) error { +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []DeleteMw, input *e.DeleteDto) error { for _, middleware := range middlewares { - logData := pu.GetLogData(input, data) + logData := pu.GetLogData(input, nil) pl.SetLogInfo(me.Event, logData, "started", middleware.Name) - if err := middleware.Func(input, data, me.Tx); err != nil { + if err := middleware.Func(input); err != nil { return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) } diff --git a/internal/use-case/main-use-case/installation/middleware.go b/internal/use-case/main-use-case/installation/middleware.go index 222ec563..dd4b0b42 100644 --- a/internal/use-case/main-use-case/installation/middleware.go +++ b/internal/use-case/main-use-case/installation/middleware.go @@ -13,4 +13,10 @@ func init() { createSimxLogMw = append(createSimxLogMw, createLogMw{Name: "create-log", Func: plugin.CreateLog}, ) + + updatePreMw = append(updatePreMw, + updateMw{Name: "update-installation", Func: plugin.Update}) + + deletePreMw = append(deletePreMw, + deleteMw{Name: "delete-installation", Func: plugin.Delete}) } diff --git a/internal/use-case/main-use-case/installation/tycovar.go b/internal/use-case/main-use-case/installation/tycovar.go index e704b3a1..262bba8c 100644 --- a/internal/use-case/main-use-case/installation/tycovar.go +++ b/internal/use-case/main-use-case/installation/tycovar.go @@ -9,8 +9,6 @@ functionality. The purpose of this is to make the code more maintainable. package installation import ( - egos "simrs-vx/internal/domain/simgos-entities/installation" - "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" @@ -19,7 +17,7 @@ import ( type createMw struct { Name string - Func func(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error + Func func(input *e.CreateDto) error } type createLogMw struct { @@ -27,6 +25,16 @@ type createLogMw struct { Func func(input *elog.SimxLogDto) error } +type updateMw struct { + Name string + Func func(input *e.UpdateDto) error +} + +type deleteMw struct { + Name string + Func func(input *e.DeleteDto) error +} + type readListMw struct { Name string Func func(input *e.ReadListDto, data *e.Installation, tx *gorm.DB) error @@ -37,8 +45,8 @@ type readDetailMw struct { Func func(input *e.ReadDetailDto, data *e.Installation, tx *gorm.DB) error } -type UpdateMw = readDetailMw -type DeleteMw = readDetailMw +type UpdateMw = updateMw +type DeleteMw = deleteMw var createPreMw []createMw // preprocess middleware var createPostMw []createMw // postprocess middleware @@ -47,7 +55,7 @@ var readListPreMw []readListMw // .. var readListPostMw []readListMw // .. var readDetailPreMw []readDetailMw var readDetailPostMw []readDetailMw -var updatePreMw []readDetailMw +var updatePreMw []updateMw var updatePostMw []readDetailMw -var deletePreMw []readDetailMw +var deletePreMw []deleteMw var deletePostMw []readDetailMw diff --git a/internal/use-case/simgos-sync-plugin/installation/plugin.go b/internal/use-case/simgos-sync-plugin/installation/plugin.go index b566302b..06c9bebe 100644 --- a/internal/use-case/simgos-sync-plugin/installation/plugin.go +++ b/internal/use-case/simgos-sync-plugin/installation/plugin.go @@ -7,54 +7,14 @@ import ( "io" "net/http" - "gorm.io/gorm" - sync "simrs-vx/internal/infra/sync-cfg" e "simrs-vx/internal/domain/main-entities/installation" - egos "simrs-vx/internal/domain/simgos-entities/installation" elog "simrs-vx/internal/domain/sync-entities/log" ) -func Create(input *e.CreateDto, data *egos.MInstalasi, tx *gorm.DB) error { - var endpoint = fmt.Sprintf("%s%s/v1/installation", sync.O.Host, sync.O.Prefix) - - jsonData, err := json.Marshal(input) - if err != nil { - return fmt.Errorf("failed to encode JSON: %w", err) - } - - req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf(resp.Status) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - if err := json.Unmarshal(body, &data); err != nil { - return fmt.Errorf("failed to parse response JSON: %w", err) - } - - return nil -} - -func CreateLog(input *elog.SimxLogDto) error { - var endpoint = fmt.Sprintf("%s%s/v1/installation/log", sync.O.Host, sync.O.Prefix) +func Create(input *e.CreateDto) error { + endpoint := getPrefixEndpoint() jsonData, err := json.Marshal(input) if err != nil { @@ -85,3 +45,109 @@ func CreateLog(input *elog.SimxLogDto) error { return nil } + +func CreateLog(input *elog.SimxLogDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := prefixEndpoint + "/log" + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonData)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Update(input *e.UpdateDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("PATCH", endpoint, bytes.NewReader(jsonData)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func Delete(input *e.DeleteDto) error { + prefixEndpoint := getPrefixEndpoint() + endpoint := fmt.Sprintf("%s/%v", prefixEndpoint, *input.Id) + + jsonData, err := json.Marshal(input) + if err != nil { + return fmt.Errorf("failed to encode JSON: %w", err) + } + + req, err := http.NewRequest("DELETE", endpoint, bytes.NewReader(jsonData)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf(resp.Status) + } + + _, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + + return nil +} + +func getPrefixEndpoint() string { + return fmt.Sprintf("%s%s/v1/installation", sync.O.Host, sync.O.Prefix) +} diff --git a/internal/use-case/simgos-sync-use-case/installation/case.go b/internal/use-case/simgos-sync-use-case/installation/case.go index f38cca14..6c3a79bd 100644 --- a/internal/use-case/simgos-sync-use-case/installation/case.go +++ b/internal/use-case/simgos-sync-use-case/installation/case.go @@ -1,14 +1,14 @@ package installation import ( + "errors" + esimgos "simrs-vx/internal/domain/simgos-entities/installation" esync "simrs-vx/internal/domain/sync-entities/log" - "strconv" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" @@ -18,6 +18,11 @@ import ( const source = "installation" func Create(input e.CreateDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + ) + event := pl.Event{ Feature: "Create", Source: source, @@ -26,10 +31,6 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - // Define trx - txSync := dg.I.Begin() - txSimgos := dg.IS["simrs"].Begin() - // STEP 1: Insert to simgos sgData, err := CreateSimgosData(input, &event, txSimgos) if err != nil { @@ -37,7 +38,7 @@ func Create(input e.CreateDto) (*d.Data, error) { } // STEP 2: Insert to Link - if err = CreateInstallationLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { + if err = CreateLinkData(*input.Id, sgData.No_Instalasi, &event, txSync); err != nil { txSimgos.Rollback() return nil, err } @@ -65,7 +66,6 @@ func Create(input e.CreateDto) (*d.Data, error) { "structure": "single-data", "status": "created", }, - Data: sgData, }, nil } @@ -80,7 +80,7 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { err := dg.I.Transaction(func(tx *gorm.DB) error { // InsertSimxLog - if err := CreateSimxLogData(input, &event, tx); err != nil { + if err := CreateLogData(input, &event, tx); err != nil { return err } @@ -93,6 +93,8 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { return nil, err } + pl.SetLogInfo(&event, nil, "complete") + return &d.Data{ Meta: d.II{ "source": source, @@ -102,212 +104,152 @@ func CreateSimxLog(input esync.SimxLogDto) (*d.Data, error) { }, nil } -func ReadList(input e.ReadListDto) (*d.Data, error) { - var data *e.Installation - var dataList []e.Installation - var metaList *e.MetaDto - var err error +func Update(input e.UpdateDto) (*d.Data, error) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MInstalasi + err error + ) event := pl.Event{ - Feature: "ReadList", + Feature: "Update", Source: source, } // Start log - pl.SetLogInfo(&event, input, "started", "readList") + pl.SetLogInfo(&event, input, "started", "update") - 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 + // STEP 1: Get Installation Link + syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) + if errGetLink != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + if errGetLink != nil && errors.Is(errGetLink, gorm.ErrRecordNotFound) { + // STEP 2.1: Insert to simgos + sgData, err = CreateSimgosData(input.CreateDto, &event, txSimgos) + if err != nil { + return nil, err } - if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { - return err + // STEP 2.2: Insert to Link + simxId := uint(*input.Id) + if err = CreateLinkData(simxId, sgData.No_Instalasi, &event, txSync); err != nil { + txSimgos.Rollback() + return nil, err } - - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { - return err + } else { + // Step 3.1: Update Simgos + err = UpdateSimgosData(input, syncLink, &event, txSimgos) + if err != nil { + return nil, err } + } - return nil - }) - - if err != nil { + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() 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.Installation - var err error - - event := pl.Event{ - Feature: "ReadDetail", - Source: source, + if errGetLink != nil { + if err = txSync.Commit().Error; err != nil { + // STEP 5.1: Rollback Partial + go func() { + _ = DeleteSimgosData(sgData, &event) + }() + return nil, err + } } - // 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 - } + pl.SetLogInfo(&event, nil, "complete") return &d.Data{ Meta: d.IS{ "source": source, "structure": "single-data", - "status": "fetched", + "status": "updated", }, - Data: data.ToResponse(), }, nil } -//func Update(input e.UpdateDto) (*d.Data, error) { -// rdDto := e.ReadDetailDto{Code: input.Code} -// var data *e.Installation -// 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 -// } -// -// if err := UpdateData(input, data, &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) { + var ( + txSync = dg.I.Begin() + txSimgos = dg.IS["simrs"].Begin() + sgData *esimgos.MInstalasi + err error + ) -//func Delete(input e.DeleteDto) (*d.Data, error) { -// rdDto := e.ReadDetailDto{Code: input.Code} -// var data *e.Installation -// 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 -// -//} + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + // STEP 1: Get Installation Link + syncLink, errGetLink := ReadDetailLinkData(*input.Id, &event) + if errGetLink != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + if errGetLink == nil { + // STEP 2: Get Simgos + sgData, err = ReadDetailSimgosData(uint16(syncLink.Simgos_Id), &event) + if err != nil { + return nil, err + } + + // STEP 3: Delete M_Instalation Simgos + err = DeleteSimgosData(sgData, &event, txSimgos) + if err != nil { + return nil, err + } + + // STEP 4: Delete Installation Link + err = DeleteLinkData(syncLink, &event, txSync) + if err != nil { + txSimgos.Rollback() + return nil, err + } + + // STEP 4: Commit two trx + if err = txSimgos.Commit().Error; err != nil { + txSimgos.Rollback() + txSync.Rollback() + return nil, err + } + + if err = txSync.Commit().Error; err != nil { + // STEP 5: Rollback Partial + go func() { + inputRollback := e.UpdateDto{ + CreateDto: e.CreateDto{ + Name: sgData.Nama_Instalasi, + }, + } + _ = UpdateSimgosData(inputRollback, syncLink, &event) + }() + return nil, err + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + }, nil + +} diff --git a/internal/use-case/simgos-sync-use-case/installation/lib.go b/internal/use-case/simgos-sync-use-case/installation/lib.go index b260776e..3e7ca854 100644 --- a/internal/use-case/simgos-sync-use-case/installation/lib.go +++ b/internal/use-case/simgos-sync-use-case/installation/lib.go @@ -1,18 +1,17 @@ package installation import ( - esync "simrs-vx/internal/domain/sync-entities/log" + esynclog "simrs-vx/internal/domain/sync-entities/log" plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" "time" dg "github.com/karincake/apem/db-gorm-pg" - gh "github.com/karincake/getuk" "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/installation" esimgos "simrs-vx/internal/domain/simgos-entities/installation" + esync "simrs-vx/internal/domain/sync-entities/installation" ) var now = time.Now() @@ -37,26 +36,76 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi return &data, nil } -func CreateSimxLogData(input esync.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { - pl.SetLogInfo(event, nil, "started", "DBCreate") - data := setDataSimxLog(&input) +func ReadDetailSimgosData(simgosId uint16, event *pl.Event) (*esimgos.MInstalasi, error) { + pl.SetLogInfo(event, simgosId, "started", "DBReadDetail") + data := esimgos.MInstalasi{} + + var tx = dg.IS["simrs"] + + if err := tx. + Where("\"no_instalasi\" = ?", simgosId). + First(&data).Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateSimgosData(input e.UpdateDto, dataSimgos *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, input, "started", "DBUpdate") + + data := setDataSimgos(&input) + data.No_Instalasi = dataSimgos.Simgos_Id var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { - tx = dg.I + tx = dg.IS["simrs"] } - if err := tx.Create(&data).Error; err != nil { - return plh.HandleCreateError(input, event, err) + 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 CreateInstallationLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { +func DeleteSimgosData(data *esimgos.MInstalasi, 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.IS["simrs"] + } + + data.St_Aktif = 0 + + if err := tx.Save(&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 +} + +func CreateLinkData(simxId, simgosId uint, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "DBCreate") data := setDataSimxLink(simxId, simgosId) @@ -75,101 +124,31 @@ func CreateInstallationLinkData(simxId, simgosId uint, event *pl.Event, dbx ...* return nil } -func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Installation, *e.MetaDto, error) { - pl.SetLogInfo(event, input, "started", "DBReadList") - data := []e.Installation{} - pagination := gh.Pagination{} - count := int64(0) - meta := e.MetaDto{} +func ReadDetailLinkData(simxId uint16, event *pl.Event) (*esync.InstallationLink, error) { + pl.SetLogInfo(event, simxId, "started", "DBReadDetail") + data := esync.InstallationLink{} - var tx *gorm.DB - if len(dbx) > 0 { - tx = dbx[0] - } else { - tx = dg.I - } - - tx = tx. - Model(&e.Installation{}). - Scopes(gh.Preload(input.Includes)). - Scopes(gh.Filter(input.FilterDto)). - Count(&count). - Scopes(gh.Paginate(input, &pagination)). - Scopes(gh.Sort(input.Sort)) - - if err := tx.Find(&data).Error; err != nil { - if 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.Installation, error) { - pl.SetLogInfo(event, input, "started", "DBReadDetail") - data := e.Installation{} - - var tx *gorm.DB - if len(dbx) > 0 { - tx = dbx[0] - } else { - tx = dg.I - } - - if input.Code != nil { - tx = tx.Where("\"Code\" = ?", *input.Code) - } - if input.Id != nil { - tx = tx.Where("\"Id\" = ?", *input.Id) - } + var tx = dg.I if err := tx. - Scopes(gh.Preload(input.Includes)). + Where("\"Simx_Id\" = ?", simxId). First(&data).Error; err != nil { - if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { - return nil, processedErr - } + return nil, err } pl.SetLogInfo(event, nil, "complete") return &data, nil } -//func UpdateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { -// pl.SetLogInfo(event, input, "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 DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event) error { +func DeleteLinkData(data *esync.InstallationLink, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBDelete") - var tx = dg.IS["simrs"] + + 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" @@ -184,3 +163,22 @@ func DeleteSimgosData(data *esimgos.MInstalasi, event *pl.Event) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func CreateLogData(input esynclog.SimxLogDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + data := setDataSimxLog(&input) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} From a053a351147b64f2b6125066236239fee537a455 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 14 Nov 2025 10:08:49 +0700 Subject: [PATCH 08/20] update sync --- cmd/main-api/config.yml-example | 6 +++++- internal/interface/migration/simgos-sync-entities.go | 5 ----- .../interface/simgos-sync-handler/simgos-sync-handler.go | 3 --- 3 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 internal/interface/migration/simgos-sync-entities.go diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index 60b1644c..448cff66 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -66,4 +66,8 @@ corsCfg: allowedMethod: bpjsCfg: - baseUrl: \ No newline at end of file + baseUrl: + +syncUrlCfg: + host: + prefix: new-to-old \ No newline at end of file diff --git a/internal/interface/migration/simgos-sync-entities.go b/internal/interface/migration/simgos-sync-entities.go deleted file mode 100644 index c6b51988..00000000 --- a/internal/interface/migration/simgos-sync-entities.go +++ /dev/null @@ -1,5 +0,0 @@ -package migration - -func getSimgosSync() []any { - return []any{} -} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go index 71002949..ce2b4ab9 100644 --- a/internal/interface/simgos-sync-handler/simgos-sync-handler.go +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -6,8 +6,6 @@ import ( /******************** infra ********************/ gs "simrs-vx/internal/infra/gorm-setting" simgosdb "simrs-vx/internal/infra/simgos-db" - simgossync "simrs-vx/internal/infra/sync-cfg" - /******************** pkg ********************/ cmw "simrs-vx/pkg/cors-manager-mw" lh "simrs-vx/pkg/lang-helper" @@ -28,7 +26,6 @@ func SetRoutes() http.Handler { a.RegisterExtCall(gs.Adjust) a.RegisterExtCall(zlc.Adjust) a.RegisterExtCall(lh.Populate) - a.RegisterExtCall(simgossync.SetConfig) a.RegisterExtCall(simgosdb.SetInstance) r := http.NewServeMux() From d4c3b0e31ccc5f62241321615c9425fad974ed7a Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 10:11:10 +0700 Subject: [PATCH 09/20] feat (user): status_code static as active --- internal/use-case/main-use-case/user/helper.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/use-case/main-use-case/user/helper.go b/internal/use-case/main-use-case/user/helper.go index acb32331..ed500bac 100644 --- a/internal/use-case/main-use-case/user/helper.go +++ b/internal/use-case/main-use-case/user/helper.go @@ -18,6 +18,7 @@ import ( ee "simrs-vx/internal/domain/main-entities/employee" esi "simrs-vx/internal/domain/main-entities/specialist-intern" e "simrs-vx/internal/domain/main-entities/user" + erc "simrs-vx/internal/domain/references/common" erg "simrs-vx/internal/domain/references/organization" ) @@ -29,7 +30,7 @@ func setCreate(src e.CreateDto, dst *e.User) error { dst.Name = src.Name dst.Password = pass - dst.Status_Code = src.Status_Code + dst.Status_Code = erc.USCActive dst.ContractPosition_Code = src.ContractPosition_Code return nil From 07e3c3b707056d47b372db6f178c2f62f8d8fda6 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 10:37:21 +0700 Subject: [PATCH 10/20] fix (encounter): missmatch position checking --- internal/lib/auth/tycovar.go | 4 ++ .../use-case/main-use-case/encounter/case.go | 39 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/internal/lib/auth/tycovar.go b/internal/lib/auth/tycovar.go index 3567cd61..70a6fec2 100644 --- a/internal/lib/auth/tycovar.go +++ b/internal/lib/auth/tycovar.go @@ -92,3 +92,7 @@ func (a AuthInfo) IsNurseIntern() bool { } return *a.Intern_Position_Code == string(ero.IPCNurse) } + +func (a AuthInfo) HasEmployeePosition() bool { + return a.Employee_Position_Code != nil +} diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 8a006a35..e3d564ff 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -78,8 +78,19 @@ func Create(input e.CreateDto) (*d.Data, error) { } } + // check if user has employee position + if !input.AuthInfo.HasEmployeePosition() { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user has no employee position", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } + // check only user with registration position is allowed to create encounter - if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + if input.AuthInfo.Employee_Position_Code != nil && *input.AuthInfo.Employee_Position_Code != string(erg.EPCReg) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "auth-forbidden", @@ -328,8 +339,19 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + // check if user has employee position + if !input.AuthInfo.HasEmployeePosition() { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user has no employee position", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } + // check only user with registration position is allowed to create encounter - if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + if input.AuthInfo.Employee_Position_Code != nil && *input.AuthInfo.Employee_Position_Code != string(erg.EPCReg) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "auth-forbidden", @@ -517,7 +539,18 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { string(erg.EPCDoc), } - if !pu.Contains(roleAllowedToCancel, input.AuthInfo.User_ContractPosition_Code) { + // check if user has employee position + if !input.AuthInfo.HasEmployeePosition() { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user has no employee position", + Raw: errors.New("authentication failed"), + } + return pl.SetLogError(&event, input) + } + + if input.AuthInfo.Employee_Position_Code != nil && !pu.Contains(roleAllowedToCancel, *input.AuthInfo.Employee_Position_Code) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "auth-forbidden", From b604216649a42d20fee2063796b35ebd3551f5e1 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 14 Nov 2025 11:27:43 +0700 Subject: [PATCH 11/20] adjust upload --- .../encounter-document/handler.go | 71 +++++++++++++++++++ .../interface/main-handler/main-handler.go | 11 +-- .../interface/main-handler/upload/handler.go | 5 +- .../main-use-case/encounter-document/case.go | 13 ---- .../use-case/main-use-case/upload/case.go | 1 + .../use-case/main-use-case/upload/helper.go | 43 +++++++---- 6 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 internal/interface/main-handler/encounter-document/handler.go diff --git a/internal/interface/main-handler/encounter-document/handler.go b/internal/interface/main-handler/encounter-document/handler.go new file mode 100644 index 00000000..60fa6d6a --- /dev/null +++ b/internal/interface/main-handler/encounter-document/handler.go @@ -0,0 +1,71 @@ +package encounter_document + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + e "simrs-vx/internal/domain/main-entities/encounter-document" + u "simrs-vx/internal/use-case/main-use-case/encounter-document" +) + +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 = uint16(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 = uint16(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 = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 346436d0..c834d963 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -2,6 +2,7 @@ package handler import ( "net/http" + encounterdocument "simrs-vx/internal/interface/main-handler/encounter-document" /******************** main / transaction ********************/ adime "simrs-vx/internal/interface/main-handler/adime" @@ -72,6 +73,7 @@ import ( /******************** sources ********************/ device "simrs-vx/internal/interface/main-handler/device" diagnosesrc "simrs-vx/internal/interface/main-handler/diagnose-src" + district "simrs-vx/internal/interface/main-handler/district" division "simrs-vx/internal/interface/main-handler/division" divisionposition "simrs-vx/internal/interface/main-handler/division-position" doctorfee "simrs-vx/internal/interface/main-handler/doctor-fee" @@ -95,7 +97,10 @@ import ( medicinegroup "simrs-vx/internal/interface/main-handler/medicine-group" medicinemethod "simrs-vx/internal/interface/main-handler/medicine-method" pharmacycompany "simrs-vx/internal/interface/main-handler/pharmacy-company" + postalregion "simrs-vx/internal/interface/main-handler/postal-region" proceduresrc "simrs-vx/internal/interface/main-handler/procedure-src" + province "simrs-vx/internal/interface/main-handler/province" + regency "simrs-vx/internal/interface/main-handler/regency" specialist "simrs-vx/internal/interface/main-handler/specialist" specialistposition "simrs-vx/internal/interface/main-handler/specialist-position" subspecialist "simrs-vx/internal/interface/main-handler/subspecialist" @@ -106,11 +111,6 @@ import ( uom "simrs-vx/internal/interface/main-handler/uom" vehicle "simrs-vx/internal/interface/main-handler/vehicle" vehiclehist "simrs-vx/internal/interface/main-handler/vehicle-hist" - - district "simrs-vx/internal/interface/main-handler/district" - postalregion "simrs-vx/internal/interface/main-handler/postal-region" - province "simrs-vx/internal/interface/main-handler/province" - regency "simrs-vx/internal/interface/main-handler/regency" village "simrs-vx/internal/interface/main-handler/village" ///// Internal @@ -273,6 +273,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/therapy-protocol", therapyprotocol.O) hc.RegCrud(r, "/v1/chemo-protocol", chemoprotocol.O) hc.RegCrud(r, "/v1/upload", upload.O) + hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/interface/main-handler/upload/handler.go b/internal/interface/main-handler/upload/handler.go index 9f1f45a0..abf8919d 100644 --- a/internal/interface/main-handler/upload/handler.go +++ b/internal/interface/main-handler/upload/handler.go @@ -6,11 +6,11 @@ import ( eru "simrs-vx/internal/domain/references/upload" + e "simrs-vx/internal/domain/main-entities/upload" + d "github.com/karincake/dodol" rw "github.com/karincake/risoles" - e "simrs-vx/internal/domain/main-entities/upload" - u "simrs-vx/internal/use-case/main-use-case/upload" ) @@ -86,4 +86,5 @@ func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { } func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + } diff --git a/internal/use-case/main-use-case/encounter-document/case.go b/internal/use-case/main-use-case/encounter-document/case.go index 206b5e1d..88592db7 100644 --- a/internal/use-case/main-use-case/encounter-document/case.go +++ b/internal/use-case/main-use-case/encounter-document/case.go @@ -244,13 +244,6 @@ func Delete(input e.DeleteDto) (*d.Data, error) { 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 := removeUploadedFile(string(eru.ETCEncounter), *data.FilePath, &event); err != nil { return err } @@ -259,12 +252,6 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } - mwRunner.setMwType(pu.MWTPost) - // Run post-middleware - if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { - return err - } - return nil }) diff --git a/internal/use-case/main-use-case/upload/case.go b/internal/use-case/main-use-case/upload/case.go index 40ff74f6..0d11a308 100644 --- a/internal/use-case/main-use-case/upload/case.go +++ b/internal/use-case/main-use-case/upload/case.go @@ -4,6 +4,7 @@ import ( "errors" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" + "strconv" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" diff --git a/internal/use-case/main-use-case/upload/helper.go b/internal/use-case/main-use-case/upload/helper.go index 652d80fa..0ddfd220 100644 --- a/internal/use-case/main-use-case/upload/helper.go +++ b/internal/use-case/main-use-case/upload/helper.go @@ -164,29 +164,44 @@ func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee. Upload_Employee_Id: input.Upload_Employee_Id, } - if len(dataUpload) > 0 { - if err := removeUploadedFile(string(input.EntityType_Code), *dataUpload[0].FilePath, *dataUpload[0].Encounter_Id, event); err != nil { - return nil, err + if input.Type_Code == eru.UCSEP || input.Type_Code == eru.UCSIPP { + if len(dataUpload) > 0 { + data = dataUpload[0] + + // remove file + if err := removeUploadedFile(string(input.EntityType_Code), *data.FilePath, *data.Encounter_Id, event); err != nil { + return nil, err + } + + // update data + err = ue.UpdateData(ee.UpdateDto{ + Id: uint16(data.Id), + CreateDto: createDto}, &data, event, tx) + if err != nil { + return nil, err + } + + data.FilePath = &input.FilePath + + return &data, nil } - // update data - err = ue.UpdateData(ee.UpdateDto{ - Id: uint16(dataUpload[0].Id), - CreateDto: createDto}, &dataUpload[0], event, tx) - if err != nil { - return nil, err - } - - data = dataUpload[0] - data.FilePath = &input.FilePath - } else { // insert data resData, err := ue.CreateData(createDto, event, tx) if err != nil { return nil, err } data = *resData + + return &data, nil } + // insert data + resData, err := ue.CreateData(createDto, event, tx) + if err != nil { + return nil, err + } + data = *resData + return &data, nil } From 85761d9be4c8ab2785149c3a572ca65d4216c189 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 14 Nov 2025 12:03:18 +0700 Subject: [PATCH 12/20] adjust upload --- internal/interface/main-handler/main-handler.go | 2 +- internal/interface/main-handler/upload/handler.go | 7 +++---- internal/use-case/main-use-case/upload/helper.go | 9 --------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 707f6fd6..b6edbbee 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -2,7 +2,6 @@ package handler import ( "net/http" - encounterdocument "simrs-vx/internal/interface/main-handler/encounter-document" "simrs-vx/internal/interface/main-handler/upload" /******************** main / transaction ********************/ @@ -18,6 +17,7 @@ import ( deviceorder "simrs-vx/internal/interface/main-handler/device-order" 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" 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" diff --git a/internal/interface/main-handler/upload/handler.go b/internal/interface/main-handler/upload/handler.go index abf8919d..72a71fbf 100644 --- a/internal/interface/main-handler/upload/handler.go +++ b/internal/interface/main-handler/upload/handler.go @@ -4,13 +4,12 @@ import ( "net/http" "strconv" - eru "simrs-vx/internal/domain/references/upload" - - e "simrs-vx/internal/domain/main-entities/upload" - d "github.com/karincake/dodol" rw "github.com/karincake/risoles" + eru "simrs-vx/internal/domain/references/upload" + + e "simrs-vx/internal/domain/main-entities/upload" u "simrs-vx/internal/use-case/main-use-case/upload" ) diff --git a/internal/use-case/main-use-case/upload/helper.go b/internal/use-case/main-use-case/upload/helper.go index 0ddfd220..e9e0ff29 100644 --- a/internal/use-case/main-use-case/upload/helper.go +++ b/internal/use-case/main-use-case/upload/helper.go @@ -185,15 +185,6 @@ func setEncounterDocument(input e.CreateDto, event *pl.Event, tx *gorm.DB) (*ee. return &data, nil } - - // insert data - resData, err := ue.CreateData(createDto, event, tx) - if err != nil { - return nil, err - } - data = *resData - - return &data, nil } // insert data From 12108e11764cc299388650a10dea8493dbd93357 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 13:43:27 +0700 Subject: [PATCH 13/20] add status code therapy protocol --- .../migrations/20251114062746.sql | 2 ++ cmd/main-migration/migrations/atlas.sum | 5 ++-- .../main-entities/therapy-protocol/entity.go | 25 ++++++++++--------- 3 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 cmd/main-migration/migrations/20251114062746.sql diff --git a/cmd/main-migration/migrations/20251114062746.sql b/cmd/main-migration/migrations/20251114062746.sql new file mode 100644 index 00000000..cdcfb350 --- /dev/null +++ b/cmd/main-migration/migrations/20251114062746.sql @@ -0,0 +1,2 @@ +-- Modify "TherapyProtocol" table +ALTER TABLE "public"."TherapyProtocol" ADD COLUMN "Status_Code" character varying(10) NULL; diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index 1c172e0c..d2484ac6 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:RvRW5FTVb8Zdn/J5rSwIrXB7cbCmWJBNaVsxqArNl4E= +h1:ZxTxK8LgNDXKTGBTF9olozXM0T7DDjK1+OJiwUwomeY= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= @@ -116,4 +116,5 @@ h1:RvRW5FTVb8Zdn/J5rSwIrXB7cbCmWJBNaVsxqArNl4E= 20251111082257.sql h1:ZsdLY1ROouos0l3oS0lkeSiuKLEUGbVvBhpcM2AVhkw= 20251111111017.sql h1:qrJ93dNtQwcuAvpsP/lAK/H63C4cinXrsVaPmWsTqkU= 20251113101344.sql h1:xaOZvAUP1fFfnO+syEFOzJUIg5lTfBe5AWHPbBWuCLA= -20251113120533.sql h1:MOk7YBwVLRCoqgPrC/mQAarp5WRl4aTk6NAzIoQPMNc= +20251113120533.sql h1:f3/U1Ve2yF2zSMhkt+xtwF8wUYfUKYwgbNeGfE37EW4= +20251114062746.sql h1:4ypWL0cP2AUJLVHhPKo3GAQ7uqolL6F8o4nUW32KnCI= diff --git a/internal/domain/main-entities/therapy-protocol/entity.go b/internal/domain/main-entities/therapy-protocol/entity.go index 7caaf0a6..1378eb25 100644 --- a/internal/domain/main-entities/therapy-protocol/entity.go +++ b/internal/domain/main-entities/therapy-protocol/entity.go @@ -16,16 +16,17 @@ type TherapyProtocol struct { Doctor_Code *string `json:"doctor_code"` Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` - Anamnesis *string `json:"anamnesis" gorm:"size:2048"` - MedicalDiagnoses *string `json:"medicalDiagnoses"` - FunctionDiagnoses *string `json:"functionDiagnoses"` - Procedures *string `json:"procedures"` - SupportingExams *string `json:"supportingExams" gorm:"size:2048"` - Instruction *string `json:"instruction" gorm:"size:2048"` - Evaluation *string `json:"evaluation" gorm:"size:2048"` - WorkCauseStatus *string `json:"workCauseStatus" gorm:"size:2048"` - Frequency *uint `json:"frequency"` - IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code" gorm:"size:10"` - Duration *uint `json:"duration"` - DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" gorm:"size:10"` + Anamnesis *string `json:"anamnesis" gorm:"size:2048"` + MedicalDiagnoses *string `json:"medicalDiagnoses"` + FunctionDiagnoses *string `json:"functionDiagnoses"` + Procedures *string `json:"procedures"` + SupportingExams *string `json:"supportingExams" gorm:"size:2048"` + Instruction *string `json:"instruction" gorm:"size:2048"` + Evaluation *string `json:"evaluation" gorm:"size:2048"` + WorkCauseStatus *string `json:"workCauseStatus" gorm:"size:2048"` + Frequency *uint `json:"frequency"` + IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code" gorm:"size:10"` + Duration *uint `json:"duration"` + DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" gorm:"size:10"` + Status_Code *common.DataVerifiedCode `json:"status_code" gorm:"size:10"` } From b51606b408db18ed9e39ebac99ac69bae01f069e Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 13:45:20 +0700 Subject: [PATCH 14/20] add makefile command --- cmd/main-migration/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/main-migration/Makefile b/cmd/main-migration/Makefile index f445a815..a93bb244 100644 --- a/cmd/main-migration/Makefile +++ b/cmd/main-migration/Makefile @@ -16,3 +16,7 @@ apply: ## Calculate the schema hash hash: atlas migrate hash + +## Apply non-linear +apply-non-linear: + atlas migrate apply --env $(ENV) --exec-order non-linear \ No newline at end of file From d03c1c7a93093e4eebe56a356343fb96d82d9946 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 14 Nov 2025 14:16:07 +0700 Subject: [PATCH 15/20] feat (therapy-protocol): add verification --- .../main-entities/therapy-protocol/dto.go | 74 +++++++++++-------- .../main-entities/therapy-protocol/entity.go | 4 + .../interface/main-handler/main-handler.go | 19 +++-- .../main-handler/therapy-protocol/handler.go | 46 ++++++++++++ .../main-use-case/therapy-protocol/case.go | 65 ++++++++++++++++ 5 files changed, 167 insertions(+), 41 deletions(-) diff --git a/internal/domain/main-entities/therapy-protocol/dto.go b/internal/domain/main-entities/therapy-protocol/dto.go index 40633b89..a1d311dc 100644 --- a/internal/domain/main-entities/therapy-protocol/dto.go +++ b/internal/domain/main-entities/therapy-protocol/dto.go @@ -4,24 +4,27 @@ import ( ecore "simrs-vx/internal/domain/base-entities/core" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" - "simrs-vx/internal/domain/references/common" + erc "simrs-vx/internal/domain/references/common" + + pa "simrs-vx/internal/lib/auth" ) type CreateDto struct { - Encounter_Id *uint `json:"encounter_id"` - Doctor_Code *string `json:"doctor_code"` - Anamnesis *string `json:"anamnesis" validate:"maxLength=2048"` - MedicalDiagnoses *string `json:"medicalDiagnoses"` - FunctionDiagnoses *string `json:"functionDiagnoses"` - Procedures *string `json:"procedures"` - SupportingExams *string `json:"supportingExams" validate:"maxLength=2048"` - Instruction *string `json:"instruction" validate:"maxLength=2048"` - Evaluation *string `json:"evaluation" validate:"maxLength=2048"` - WorkCauseStatus *string `json:"workCauseStatus"` - Frequency *uint `json:"frequency"` - IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code" validate:"maxLength=10"` - Duration *uint `json:"duration"` - DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" validate:"maxLength=10"` + Encounter_Id *uint `json:"encounter_id"` + Doctor_Code *string `json:"doctor_code"` + Anamnesis *string `json:"anamnesis" validate:"maxLength=2048"` + MedicalDiagnoses *string `json:"medicalDiagnoses"` + FunctionDiagnoses *string `json:"functionDiagnoses"` + Procedures *string `json:"procedures"` + SupportingExams *string `json:"supportingExams" validate:"maxLength=2048"` + Instruction *string `json:"instruction" validate:"maxLength=2048"` + Evaluation *string `json:"evaluation" validate:"maxLength=2048"` + WorkCauseStatus *string `json:"workCauseStatus"` + Frequency *uint `json:"frequency"` + IntervalUnit_Code *erc.TimeUnitCode `json:"intervalUnit_code" validate:"maxLength=10"` + Duration *uint `json:"duration"` + DurationUnit_Code *erc.TimeUnitCode `json:"durationUnit_code" validate:"maxLength=10"` + Status_Code *erc.DataVerifiedCode `json:"status_code"` } type ReadListDto struct { @@ -49,6 +52,13 @@ type DeleteDto struct { Id uint `json:"id"` } +type VerifyDto struct { + Id uint `json:"id"` + Status_Code erc.DataVerifiedCode `json:"status_code"` + + pa.AuthInfo +} + type MetaDto struct { PageNumber int `json:"page_number"` PageSize int `json:"page_size"` @@ -57,22 +67,23 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty"` - Doctor_Code *string `json:"doctor_code"` - Doctor *ed.Doctor `json:"doctor,omitempty"` - Anamnesis *string `json:"anamnesis"` - MedicalDiagnoses *string `json:"medicalDiagnoses"` - FunctionDiagnoses *string `json:"functionDiagnoses"` - Procedures *string `json:"procedures"` - SupportingExams *string `json:"supportingExams"` - Instruction *string `json:"instruction"` - Evaluation *string `json:"evaluation"` - WorkCauseStatus *string `json:"workCauseStatus"` - Frequency *uint `json:"frequency"` - IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code"` - Duration *uint `json:"duration"` - DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code"` + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty"` + Doctor_Code *string `json:"doctor_code"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + Anamnesis *string `json:"anamnesis"` + MedicalDiagnoses *string `json:"medicalDiagnoses"` + FunctionDiagnoses *string `json:"functionDiagnoses"` + Procedures *string `json:"procedures"` + SupportingExams *string `json:"supportingExams"` + Instruction *string `json:"instruction"` + Evaluation *string `json:"evaluation"` + WorkCauseStatus *string `json:"workCauseStatus"` + Frequency *uint `json:"frequency"` + IntervalUnit_Code *erc.TimeUnitCode `json:"intervalUnit_code"` + Duration *uint `json:"duration"` + DurationUnit_Code *erc.TimeUnitCode `json:"durationUnit_code"` + Status_Code *erc.DataVerifiedCode `json:"status_code"` } func (d TherapyProtocol) ToResponse() ResponseDto { @@ -93,6 +104,7 @@ func (d TherapyProtocol) ToResponse() ResponseDto { IntervalUnit_Code: d.IntervalUnit_Code, Duration: d.Duration, DurationUnit_Code: d.DurationUnit_Code, + Status_Code: d.Status_Code, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/therapy-protocol/entity.go b/internal/domain/main-entities/therapy-protocol/entity.go index 1378eb25..e7607eea 100644 --- a/internal/domain/main-entities/therapy-protocol/entity.go +++ b/internal/domain/main-entities/therapy-protocol/entity.go @@ -30,3 +30,7 @@ type TherapyProtocol struct { DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" gorm:"size:10"` Status_Code *common.DataVerifiedCode `json:"status_code" gorm:"size:10"` } + +func (d TherapyProtocol) IsNew() bool { + return d.Status_Code != nil && *d.Status_Code == common.DVCNew +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index a06c5c18..aa6f76fb 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -152,7 +152,6 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/prescription-item", prescriptionitem.O) hc.RegCrud(r, "/v1/device-order-item", deviceorderitem.O) hc.RegCrud(r, "/v1/material-order-item", materialorderitem.O) - hk.GroupRoutes("/v1/encounter", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": encounter.O.GetList, "GET /{id}": encounter.O.GetDetail, @@ -211,7 +210,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": medication.O.Delete, "PATCH /{id}/complete": medication.O.Complete, }) - hk.GroupRoutes("/v1/medication-item", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": medicationitem.O.GetList, "GET /{id}": medicationitem.O.GetDetail, @@ -220,7 +218,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": medicationitem.O.Delete, "PATCH /{id}/redeem": medicationitem.O.Redeem, }) - hk.GroupRoutes("/v1/medication-item-dist", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": medicationitemdist.O.GetList, "GET /{id}": medicationitemdist.O.GetDetail, @@ -229,7 +226,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": medicationitemdist.O.Delete, "PATCH /{id}/consume": medicationitemdist.O.Consume, }) - hk.GroupRoutes("/v1/device-order", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": deviceorder.O.GetList, "GET /{id}": deviceorder.O.GetDetail, @@ -238,7 +234,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": deviceorder.O.Delete, "PATCH /{id}/complete": deviceorder.O.Complete, }) - hk.GroupRoutes("/v1/material-order", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": materialorder.O.GetList, "GET /{id}": materialorder.O.GetDetail, @@ -247,7 +242,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": materialorder.O.Delete, "PATCH /{id}/complete": materialorder.O.Complete, }) - hk.GroupRoutes("/v1/consultation", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": consultation.O.GetList, "GET /{id}": consultation.O.GetDetail, @@ -256,7 +250,6 @@ func SetRoutes() http.Handler { "DELETE /{id}": consultation.O.Delete, "PATCH /{id}/reply": consultation.O.Reply, }) - hk.GroupRoutes("/v1/chemo", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": chemo.O.GetList, "GET /{id}": chemo.O.GetDetail, @@ -266,13 +259,20 @@ func SetRoutes() http.Handler { "PATCH /{id}/verify": chemo.O.Verify, "PATCH /{id}/reject": chemo.O.Reject, }) - hc.RegCrud(r, "/v1/control-letter", controlletter.O) hc.RegCrud(r, "/v1/internal-reference", internalreference.O) hc.RegCrud(r, "/v1/ambulance-transport-req", ambulancetransportrequest.O) hc.RegCrud(r, "/v1/responsible-doctor-hist", responsibledoctorhist.O) hc.RegCrud(r, "/v1/adm-employee-hist", admemployeehist.O) - hc.RegCrud(r, "/v1/therapy-protocol", therapyprotocol.O) + hk.GroupRoutes("/v1/therapy-protocol", r, auth.GuardMW, hk.MapHandlerFunc{ + "GET /": therapyprotocol.O.GetList, + "GET /{id}": therapyprotocol.O.GetDetail, + "POST /": therapyprotocol.O.Create, + "PATCH /{id}": therapyprotocol.O.Update, + "DELETE /{id}": therapyprotocol.O.Delete, + "PATCH /{id}/verify": therapyprotocol.O.Verify, + "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/encounter-document", encounterdocument.O) @@ -297,7 +297,6 @@ func SetRoutes() http.Handler { "PATCH /{id}/active": user.O.Active, }) hc.RegCrud(r, "/v1/user-fes", userfes.O) - hk.GroupRoutes("/v1/patient", r, hk.MapHandlerFunc{ "GET /": patient.O.GetList, "GET /{id}": patient.O.GetDetail, diff --git a/internal/interface/main-handler/therapy-protocol/handler.go b/internal/interface/main-handler/therapy-protocol/handler.go index 2d9efc3b..769e5677 100644 --- a/internal/interface/main-handler/therapy-protocol/handler.go +++ b/internal/interface/main-handler/therapy-protocol/handler.go @@ -9,7 +9,12 @@ import ( // ua "github.com/karincake/tumpeng/auth/svc" e "simrs-vx/internal/domain/main-entities/therapy-protocol" + erc "simrs-vx/internal/domain/references/common" u "simrs-vx/internal/use-case/main-use-case/therapy-protocol" + + d "github.com/karincake/dodol" + + pa "simrs-vx/internal/lib/auth" ) type myBase struct{} @@ -71,3 +76,44 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { res, err := u.Delete(dto) rw.DataResponse(w, res, err) } + +func (obj myBase) Verify(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.VerifyDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + dto.Id = uint(id) + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } + + dto.AuthInfo = *authInfo + dto.Status_Code = erc.DVCVerified + res, err := u.Verify(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Reject(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.VerifyDto{} + dto.Id = uint(id) + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } + dto.AuthInfo = *authInfo + dto.Status_Code = erc.DVCRejected + res, err := u.Verify(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/therapy-protocol/case.go b/internal/use-case/main-use-case/therapy-protocol/case.go index bc65325f..ed4f3a21 100644 --- a/internal/use-case/main-use-case/therapy-protocol/case.go +++ b/internal/use-case/main-use-case/therapy-protocol/case.go @@ -1,6 +1,7 @@ package therapy_protocol import ( + "errors" "strconv" "gorm.io/gorm" @@ -15,6 +16,8 @@ import ( ee "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/therapy-protocol" + erc "simrs-vx/internal/domain/references/common" + ud "simrs-vx/internal/use-case/main-use-case/doctor" ue "simrs-vx/internal/use-case/main-use-case/encounter" ) @@ -45,6 +48,8 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } + statusNew := erc.DVCNew + input.Status_Code = &statusNew if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -291,6 +296,66 @@ func Delete(input e.DeleteDto) (*d.Data, error) { } +func Verify(input e.VerifyDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.TherapyProtocol + var err error + + event := pl.Event{ + Feature: "Verify", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "verify") + + 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 + } + + if !data.IsNew() { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-state-mismatch", + Detail: "data is not new", + Raw: errors.New("data is not new"), + } + return pl.SetLogError(&event, input) + } + + data.Status_Code = &input.Status_Code + err = tx.Save(&data).Error + if 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 + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "verify", + }, + Data: data.ToResponse(), + }, nil +} + func validateForeignKey(input e.CreateDto) error { // validate encounter if input.Encounter_Id != nil { From 12abbfa41a35e0de1babc47ed9a6c8e74d834bfe Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 14 Nov 2025 14:19:14 +0700 Subject: [PATCH 16/20] migration from server --- cmd/main-migration/migrations/atlas.sum | 88 ++++++++++++------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index d2484ac6..cac4826e 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:ZxTxK8LgNDXKTGBTF9olozXM0T7DDjK1+OJiwUwomeY= +h1:t3DFzssEf9liKwykDE17fo19llwCgpTgEuSUd9VGusk= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= @@ -75,46 +75,46 @@ h1:ZxTxK8LgNDXKTGBTF9olozXM0T7DDjK1+OJiwUwomeY= 20251106040137.sql h1:ppcqkVoT0o9jZcjI/TN7LuaPxXhJQhnIXEJtloP/46o= 20251106041333.sql h1:2JkxyelQ/EeB+boL5bfpnzefw32ttEGKvKchtQjWmAU= 20251106042006.sql h1:ruppYa1kAJQUU3ufQBbKGMcXrGbGJJiRPclT+dNc/YQ= -20251106050412.sql h1:1002KYtHd8AwrQTMewbs/PPHDylHDghigE/3S7PVdMA= -20251106063418.sql h1:jPW/gBnbFl4RO39lQ0ZMDtYA6xbhyD6CgQupT50HmaY= -20251106071906.sql h1:leYGKxR3EQn794aOehf0sd/ZPmOnvBMZPy5/anGmRB4= -20251106073157.sql h1:KASMzjjjk5UB7Zj8lCRtM1utc4ZnDjlnpZbtTe3vONE= -20251106074218.sql h1:Z5q5deOvLaZDPhiVTN9st3/s56RepBa2YOyrMXBdj4A= -20251106081846.sql h1:P+VsWwhGt60adDIZuE/Aa38JVp/yX1rnsdpXpxASodw= -20251106082844.sql h1:Dmi5A8i9frQZvdXYPwc7f8CisZtBH8liSXq1rI6z1iM= -20251106090021.sql h1:4JwdKgO8T46YhyWVJUxpRIwudBDlG8QN1brSOYmgQ20= -20251106144745.sql h1:nqnQCzGrVJaq8ilOEOGXeRUL1dolj+OPWKuP8A92FRA= -20251107012049.sql h1:Pff4UqltGS3clSlGr0qq8CQM56L29wyxY0FC/N/YAhU= -20251107064812.sql h1:GB9a0ZfMYTIoGNmKUG+XcYUsTnRMFfT4/dAD71uCPc4= -20251107064937.sql h1:IC5pw1Ifj30hiE6dr5NMHXaSHoQI+vRd40N5ABgBHRI= -20251107071420.sql h1:9NO3iyLEXEtWa2kSRjM/8LyzuVIk6pdFL2SuheWjB08= -20251107074318.sql h1:7fHbSRrdjOmHh/xwnjCLwoiB5cW5zeH+uxLV0vZbkIA= -20251107075050.sql h1:np+3uTOnU9QNtK7Knaw8eRMhkyB9AwrtSNHphOBxbHY= -20251107080604.sql h1:cXDBLPJDVWLTG6yEJqkJsOQ7p7VYxLM2SY+mwO8qSHo= -20251107081830.sql h1:/S7OQZo4ZnK80t28g/JyiOTZtmWG/dP5Wg2zXNMQ/iE= -20251107091033.sql h1:/cbkF1nO/IjNSIfDJJx456KJtQ9rWFXOBFAkR/M2xiE= -20251107091209.sql h1:jrLQOUeV8ji2fg0pnEcs1bw4ANUxzTSMXC/rrHLIY+M= -20251107091541.sql h1:6UqbhQQRmzA2+eKu5lIvkwOkk+lH70QLZC8Pjpjcq68= -20251110012217.sql h1:C9HpX0iyHzKjyNv/5DSAn2MCHj6MX4p5UQ/NrY7QD0w= -20251110012306.sql h1:J54yb27d30LBbYp9n1P66gFVRlxPguKu0kxmWIBBG8g= -20251110052049.sql h1:232T2x8xTczJl9nk4jxJpZXhoOGYthhxjJ7nK8Jd8vg= -20251110062042.sql h1:WnfVUXrzYoj8qdkkjO9/JQQ8agGd4GfSHQdMjo7LDAg= -20251110063202.sql h1:hSzGfwVMWa6q3vwIQZUkxKgBNCzHjB+6GKy54zfV+oQ= -20251110063633.sql h1:/VpofIAqNS1CnazEnpW/+evbzn9Kew3xDW48r57M+Xg= -20251110085551.sql h1:bFZwSmfvVbTUr/enWB82WqjG88gpqcZ6s45btUvO0uo= -20251110091516.sql h1:KkJMwPQuaZQhiqnKrNQrgP12gw9rV8T3P2o3mtGTcvY= -20251110091948.sql h1:I4odAYrJdvNf1jPw6ppDC0XdI7v6vKBACg/ABwUgA7I= -20251110092729.sql h1:l1out8soEmVP6dNjaIOtGYo6QDcoJZRI8X1sjZ5ZGmo= -20251110093522.sql h1:nsz8jCxGjEdr/bz9g+4ozfZzIP803xONjVmucad1GMc= -20251110100258.sql h1:IBqt1VZj5WjQ+l9aAFGHOCCBtzb03KlLLihFLut7itg= -20251110100545.sql h1:6/LV7751iyKxE2xI6vO1zly+aHUwxXD/IBwLcVpKxqM= -20251110155448.sql h1:kFPobJB+cpflsXBAWUwy3lohuWvrb/VRlXnhJWl7i3Y= -20251111072601.sql h1:ch8F+yVhsSM5xY+TwMLY3PxdLa4Wuhtj76oyw79R7Js= -20251111073546.sql h1:cCv0NPscADAOBahRVqtDWFs6G2t7n+4a+RwlF8vk/c4= -20251111074148.sql h1:70TsV83u1gQ5TktI13K7NQiyCCa35Td2aR6CNtKUa4U= -20251111074652.sql h1:ddfQ/sRKMezPM75xBFTGytUQX5AwZ3znrJVpg73gKPA= -20251111082257.sql h1:ZsdLY1ROouos0l3oS0lkeSiuKLEUGbVvBhpcM2AVhkw= -20251111111017.sql h1:qrJ93dNtQwcuAvpsP/lAK/H63C4cinXrsVaPmWsTqkU= -20251113101344.sql h1:xaOZvAUP1fFfnO+syEFOzJUIg5lTfBe5AWHPbBWuCLA= -20251113120533.sql h1:f3/U1Ve2yF2zSMhkt+xtwF8wUYfUKYwgbNeGfE37EW4= -20251114062746.sql h1:4ypWL0cP2AUJLVHhPKo3GAQ7uqolL6F8o4nUW32KnCI= +20251106050412.sql h1:MiEMJ1HCFYnalKuq3Z38xJeogfBAMqsTv2sG4EF8dDw= +20251106063418.sql h1:y3veDJPjKekOWLCZek/LgQwXPRhZtOppTfUXiqoL95s= +20251106071906.sql h1:/TUZA3XpMY23qEJXdkTwlzrNMvSSl6JJniPcgAttBaw= +20251106073157.sql h1:78txeibJ602DMD7huD618ZSMt6phSRzDNPTlo0PGyrc= +20251106074218.sql h1:8Xz7WywrtUnSxOHhlal53gG9rE7r86LFUt5zBFe/mIs= +20251106081846.sql h1:jp91Bf5bxGXMiUB1VIuN6y768vb2iWwow44WfCE5J5k= +20251106082844.sql h1:RHYzRO4G1fSWwf+xc/3QezZ/Iil67cZPIgNpNz3TNhQ= +20251106090021.sql h1:dFDk6mq+zjbYWmfWIrHf9DiKvvoXHjrr0++zssMTWP8= +20251106144745.sql h1:aHcr23iBFqCHer5D/SsPMXBCLjGqUYvWYfRU8jSJgIw= +20251107012049.sql h1:hu/7NHhnAkT4xK0RNtqmMDdH1Bo5EZbl7itDRjiCT+g= +20251107064812.sql h1:sfCXDQYnMf0ddrQ9oYljWJLLSt9NJjJV6o8VS3p7aZE= +20251107064937.sql h1:DlYGJ9LZFwZyR7jBP5zaGB128aIc4HAixBKPYCz9EkY= +20251107071420.sql h1:ynCdZAd2utLl+FhtWZwtahNXgIVOvuk3s/rOq7lfXA4= +20251107074318.sql h1:WE9cPhibWtZ0dbu1VEGirTeY6ijFYGMNhHdBtM32kOc= +20251107075050.sql h1:8tvneruqdynDOaJK1+0z4CH7YXZStZpGdqwIeOMLik4= +20251107080604.sql h1:8c4jd4Tql7tcdhbI9NS0tgvN+ADu9FnCf8wMUbmW7A0= +20251107081830.sql h1:SAAe3lmsm9vGXuSEsDdl7ad0EAxP5CMmFRDEgp9M7yY= +20251107091033.sql h1:JLdX/u7GUdBfjrPrMSNAqc8HtSoj0YA9iW9Vc6FJZdw= +20251107091209.sql h1:CzhYtwAwT+GHrbqcagnJE+v3mbl/rObf1IJaLCKlzrs= +20251107091541.sql h1:+3ZyWJTftDY2JeWThXuIxGWpUBnyMPyOyY4jBjdWYJI= +20251110012217.sql h1:f4Z8TuGc+XMSJ+Ekn4/PeHRE2FlHWkc5gKPJB0hAX7c= +20251110012306.sql h1:ENPyI6Kjdk6qKJQb0yJ6MCTDPAmO1WD/uhKuCSl+jYo= +20251110052049.sql h1:OrQ0acnyoQLKnTitZfnBcVr5jDslF59OFLaqT7SpdVs= +20251110062042.sql h1:9KwldQt0NpVPR86L0T4hlkfHAGau+7CiZYgu5rF+yhg= +20251110063202.sql h1:A117DuZmZ6U0jWHA3DISnr+yvBjKIr1ObrUr047YezQ= +20251110063633.sql h1:qTiC0F19JnhUIXF4LGJQ21jEV6kKGyhTr1x2kimFqPQ= +20251110085551.sql h1:HZcJM0RSC6HBaUSjKBE8MgDG8Vn9f3LmwA/OnT9Cp7I= +20251110091516.sql h1:W3AQhQLgirEWuCObbLl+Prdrbq6k6EEY1xcoWsmbog4= +20251110091948.sql h1:3tsITMrZr/T+L4wqUMz8sHS229jCJl4T0Nu3dMccxH8= +20251110092729.sql h1:uU+k88RH/e0Ns4/SmJl03RVYPscBAPuiLfxR6CJqaf0= +20251110093522.sql h1:O7upSj8VNjzvroL4IU59bfxKATOkAVGBArcUbVNq9aM= +20251110100258.sql h1://JSArUMNI3/gAtYDx2VN94C198SFW0ANjgs+p6eCRM= +20251110100545.sql h1:ENPOqeJYRpMI4e8VCKwaQgaql8se6pIidAhG2cjskBg= +20251110155448.sql h1:VW9KE0jxWy4flZ28sdXmhY6JWd6eKAX/cVL8KWRVii4= +20251111072601.sql h1:99WWzGjcDDFNC2cHRfPu4MBLWNrgPMY5HAYUbmIcVRA= +20251111073546.sql h1:iIGwFNfUgStdczw/PXTH3SD84xAyxrbZICofc3M8TMk= +20251111074148.sql h1:mzkezSKwCV2bTZ/0BHiTCX5qAy4uHpwar1xzwAH15Ko= +20251111074652.sql h1:lYh4/3BHV24pgPC0pP4RUKb+XtL/6AsZGPItRpnzBiQ= +20251111082257.sql h1:+cIZ1lf8HYGSL6t6U88plLKk8nOO1YVdV7h3YOM84ds= +20251111111017.sql h1:xzlhR2xLfOj4ddYlesS+bpEeDrxiAf5ILNOspsbZjBU= +20251113101344.sql h1:vSzThY3p6XYTj0dJ2uFVkHmlytyrFAnGE58CJLx364I= +20251113120533.sql h1:lfjgdqRGo/EohrVw8sWlFbDjh3ASTsfQ6pr3qjafqvc= +20251114062746.sql h1:6DLYjfJ60PkAABZTXvOwSE+xrU25oyoX7gpYlBnA9cw= From c19965f4c31a11e3ff3c5826cd8857dfd1b363b2 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sat, 15 Nov 2025 01:16:21 +0700 Subject: [PATCH 17/20] feat/things-munaja: device order items preload --- .../device-order-item/base/entity.go | 14 ++++++++++++++ .../main-entities/device-order-item/entity.go | 11 +++-------- .../domain/main-entities/device-order/entity.go | 15 ++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 internal/domain/main-entities/device-order-item/base/entity.go diff --git a/internal/domain/main-entities/device-order-item/base/entity.go b/internal/domain/main-entities/device-order-item/base/entity.go new file mode 100644 index 00000000..54cbe611 --- /dev/null +++ b/internal/domain/main-entities/device-order-item/base/entity.go @@ -0,0 +1,14 @@ +package base + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/device" +) + +type DeviceOrderItem struct { + ecore.Main // adjust this according to the needs + DeviceOrder_Id *uint `json:"deviceOrder_id"` + Device_Code *string `json:"device_code"` + Device *ed.Device `json:"device,omitempty" gorm:"foreignKey:Device_Code;references:Code"` + Count uint8 `json:"count"` +} diff --git a/internal/domain/main-entities/device-order-item/entity.go b/internal/domain/main-entities/device-order-item/entity.go index 27197125..a0743643 100644 --- a/internal/domain/main-entities/device-order-item/entity.go +++ b/internal/domain/main-entities/device-order-item/entity.go @@ -1,16 +1,11 @@ package deviceorderitem import ( - ecore "simrs-vx/internal/domain/base-entities/core" - ed "simrs-vx/internal/domain/main-entities/device" edo "simrs-vx/internal/domain/main-entities/device-order" + eb "simrs-vx/internal/domain/main-entities/device-order-item/base" ) type DeviceOrderItem struct { - ecore.Main // adjust this according to the needs - DeviceOrder_Id *uint `json:"deviceOrder_id"` - DeviceOrder *edo.DeviceOrder `json:"deviceOrder,omitempty" gorm:"foreignKey:DeviceOrder_Id;references:Id"` - Device_Code *string `json:"device_code"` - Device *ed.Device `json:"device,omitempty" gorm:"foreignKey:Device_Code;references:Code"` - Count uint8 `json:"count"` + eb.DeviceOrderItem + DeviceOrder *edo.DeviceOrder `json:"deviceOrder,omitempty" gorm:"foreignKey:DeviceOrder_Id;references:Id"` } diff --git a/internal/domain/main-entities/device-order/entity.go b/internal/domain/main-entities/device-order/entity.go index 7ff4fa34..d9ba5a59 100644 --- a/internal/domain/main-entities/device-order/entity.go +++ b/internal/domain/main-entities/device-order/entity.go @@ -2,19 +2,20 @@ package deviceorder import ( ecore "simrs-vx/internal/domain/base-entities/core" + edoi "simrs-vx/internal/domain/main-entities/device-order-item/base" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" - erc "simrs-vx/internal/domain/references/common" ) type DeviceOrder struct { - ecore.Main // adjust this according to the needs - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` - Doctor_Code *string `json:"doctor_code"` - Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` - Status_Code erc.DataStatusCode `json:"status_code"` + ecore.Main // adjust this according to the needs + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor_Code *string `json:"doctor_code"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + Status_Code erc.DataStatusCode `json:"status_code"` + Items []*edoi.DeviceOrderItem `json:"items" gorm:"foreignKey:DeviceOrder_Id;references:Id"` } func (d DeviceOrder) IsCompleted() bool { From 03210549bb470c260f0d6d0257bac5bb9f147fe3 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 17 Nov 2025 07:57:22 +0700 Subject: [PATCH 18/20] feat/things-munaja: adjust prescription --- .../prescription-item/base/entity.go | 25 +++++++++++++++++++ .../main-entities/prescription-item/dto.go | 1 + .../main-entities/prescription-item/entity.go | 22 +++------------- .../domain/main-entities/prescription/dto.go | 15 ++++++----- .../main-entities/prescription/entity.go | 16 ++++++------ .../main-handler/prescription/handler.go | 1 + .../main-use-case/prescription-item/lib.go | 4 +-- 7 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 internal/domain/main-entities/prescription-item/base/entity.go diff --git a/internal/domain/main-entities/prescription-item/base/entity.go b/internal/domain/main-entities/prescription-item/base/entity.go new file mode 100644 index 00000000..a5cb5c44 --- /dev/null +++ b/internal/domain/main-entities/prescription-item/base/entity.go @@ -0,0 +1,25 @@ +package prescriptionitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + em "simrs-vx/internal/domain/main-entities/medicine" + emm "simrs-vx/internal/domain/main-entities/medicine-mix" + + erc "simrs-vx/internal/domain/references/common" +) + +type PrescriptionItem struct { + ecore.Main // adjust this according to the needs + Prescription_Id *uint `json:"prescription_id"` + IsMix bool `json:"isMix"` + Medicine_Code *string `json:"medicine_code"` + Medicine *em.Medicine `json:"medicine,omitempty" gorm:"foreignKey:Medicine_Code;references:Code"` + MedicineMix_Id *uint `json:"medicineMix_id"` + MedicineMix *emm.MedicineMix `json:"medicineMix,omitempty" gorm:"foreignKey:MedicineMix_Id;references:Id"` + Frequency *uint16 `json:"frequency"` + Dose float64 `json:"dose"` + Usage string `json:"usage" gorm:"size:255"` + Interval uint8 `json:"interval"` + IntervalUnit_Code erc.TimeUnitCode `json:"intervalUnit_code"` + Quantity float64 `json:"quantity"` +} diff --git a/internal/domain/main-entities/prescription-item/dto.go b/internal/domain/main-entities/prescription-item/dto.go index 6e871428..44619ec8 100644 --- a/internal/domain/main-entities/prescription-item/dto.go +++ b/internal/domain/main-entities/prescription-item/dto.go @@ -27,6 +27,7 @@ type ReadListDto struct { FilterDto Includes string `json:"includes"` Pagination ecore.Pagination + Sort string `json:"sort"` } type FilterDto struct { diff --git a/internal/domain/main-entities/prescription-item/entity.go b/internal/domain/main-entities/prescription-item/entity.go index 93174079..465059d7 100644 --- a/internal/domain/main-entities/prescription-item/entity.go +++ b/internal/domain/main-entities/prescription-item/entity.go @@ -2,26 +2,12 @@ package prescriptionitem import ( ecore "simrs-vx/internal/domain/base-entities/core" - em "simrs-vx/internal/domain/main-entities/medicine" - emm "simrs-vx/internal/domain/main-entities/medicine-mix" ep "simrs-vx/internal/domain/main-entities/prescription" - - erc "simrs-vx/internal/domain/references/common" + epib "simrs-vx/internal/domain/main-entities/prescription-item/base" ) type PrescriptionItem struct { - ecore.Main // adjust this according to the needs - Prescription_Id *uint `json:"prescription_id"` - Prescription *ep.Prescription `json:"prescription,omitempty" gorm:"foreignKey:Prescription_Id;references:Id"` - IsMix bool `json:"isMix"` - Medicine_Code *string `json:"medicine_code"` - Medicine *em.Medicine `json:"medicine,omitempty" gorm:"foreignKey:Medicine_Code;references:Code"` - MedicineMix_Id *uint `json:"medicineMix_id"` - MedicineMix *emm.MedicineMix `json:"medicineMix,omitempty" gorm:"foreignKey:MedicineMix_Id;references:Id"` - Frequency *uint16 `json:"frequency"` - Dose float64 `json:"dose"` - Usage string `json:"usage" gorm:"size:255"` - Interval uint8 `json:"interval"` - IntervalUnit_Code erc.TimeUnitCode `json:"intervalUnit_code"` - Quantity float64 `json:"quantity"` + ecore.Main // adjust this according to the needs + epib.PrescriptionItem + Prescription *ep.Prescription `json:"prescription,omitempty" gorm:"foreignKey:Prescription_Id;references:Id"` } diff --git a/internal/domain/main-entities/prescription/dto.go b/internal/domain/main-entities/prescription/dto.go index 9c73c0ee..7c4040fa 100644 --- a/internal/domain/main-entities/prescription/dto.go +++ b/internal/domain/main-entities/prescription/dto.go @@ -6,6 +6,7 @@ import ( ecore "simrs-vx/internal/domain/base-entities/core" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" + epi "simrs-vx/internal/domain/main-entities/prescription-item/base" pa "simrs-vx/internal/lib/auth" @@ -56,12 +57,13 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty"` - Doctor_Code *string `json:"doctor_code"` - Doctor *ed.Doctor `json:"doctor,omitempty"` - IssuedAt *time.Time `json:"issuedAt"` - Status_Code erc.DataStatusCode `json:"status_code"` + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty"` + Doctor_Code *string `json:"doctor_code"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + IssuedAt *time.Time `json:"issuedAt"` + Status_Code erc.DataStatusCode `json:"status_code"` + Items []epi.PrescriptionItem `json:"items" gorm:"foreignKey:Prescription_Id;references:Id"` } func (d Prescription) ToResponse() ResponseDto { @@ -72,6 +74,7 @@ func (d Prescription) ToResponse() ResponseDto { Doctor: d.Doctor, IssuedAt: d.IssuedAt, Status_Code: d.Status_Code, + Items: d.Items, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/prescription/entity.go b/internal/domain/main-entities/prescription/entity.go index 8c879b29..f7cc3597 100644 --- a/internal/domain/main-entities/prescription/entity.go +++ b/internal/domain/main-entities/prescription/entity.go @@ -4,6 +4,7 @@ import ( ecore "simrs-vx/internal/domain/base-entities/core" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" + epi "simrs-vx/internal/domain/main-entities/prescription-item/base" "time" @@ -11,13 +12,14 @@ import ( ) type Prescription struct { - ecore.Main // adjust this according to the needs - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` - Doctor_Code *string `json:"doctor_code" gorm:"size:20"` - Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` - IssuedAt *time.Time `json:"issuedAt"` - Status_Code erc.DataStatusCode `json:"status_code"` + ecore.Main // adjust this according to the needs + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor_Code *string `json:"doctor_code" gorm:"size:20"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + IssuedAt *time.Time `json:"issuedAt"` + Status_Code erc.DataStatusCode `json:"status_code"` + Items []epi.PrescriptionItem `json:"items" gorm:"foreignKey:Prescription_Id;references:Id"` } func (d Prescription) IsNotNew() bool { diff --git a/internal/interface/main-handler/prescription/handler.go b/internal/interface/main-handler/prescription/handler.go index 30d5998e..c7649d34 100644 --- a/internal/interface/main-handler/prescription/handler.go +++ b/internal/interface/main-handler/prescription/handler.go @@ -46,6 +46,7 @@ func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { return } dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) dto.Id = uint(id) res, err := u.ReadDetail(dto) rw.DataResponse(w, res, err) diff --git a/internal/use-case/main-use-case/prescription-item/lib.go b/internal/use-case/main-use-case/prescription-item/lib.go index aa6f97bf..aa5c9e2b 100644 --- a/internal/use-case/main-use-case/prescription-item/lib.go +++ b/internal/use-case/main-use-case/prescription-item/lib.go @@ -47,13 +47,13 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Pr tx = dg.I } - tx = tx. + tx = tx.Debug(). Model(&e.PrescriptionItem{}). Scopes(gh.Preload(input.Includes)). 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 { From 65f5c6dc20229fc85d649489a7b055ce10495476 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 17 Nov 2025 07:58:22 +0700 Subject: [PATCH 19/20] feat/things-munaja: adjust DeviceOrder --- .../device-order-item/base/entity.go | 4 ++-- .../main-entities/device-order-item/dto.go | 10 +++++----- .../main-entities/device-order-item/entity.go | 2 ++ .../domain/main-entities/device-order/dto.go | 13 ++++++++----- .../domain/main-entities/device-order/entity.go | 17 +++++++++-------- .../main-use-case/device-order-item/helper.go | 2 +- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/internal/domain/main-entities/device-order-item/base/entity.go b/internal/domain/main-entities/device-order-item/base/entity.go index 54cbe611..95eaa895 100644 --- a/internal/domain/main-entities/device-order-item/base/entity.go +++ b/internal/domain/main-entities/device-order-item/base/entity.go @@ -1,4 +1,4 @@ -package base +package deviceorderitem import ( ecore "simrs-vx/internal/domain/base-entities/core" @@ -10,5 +10,5 @@ type DeviceOrderItem struct { DeviceOrder_Id *uint `json:"deviceOrder_id"` Device_Code *string `json:"device_code"` Device *ed.Device `json:"device,omitempty" gorm:"foreignKey:Device_Code;references:Code"` - Count uint8 `json:"count"` + Quantity uint8 `json:"quantity"` } diff --git a/internal/domain/main-entities/device-order-item/dto.go b/internal/domain/main-entities/device-order-item/dto.go index afa08ed8..4464c91d 100644 --- a/internal/domain/main-entities/device-order-item/dto.go +++ b/internal/domain/main-entities/device-order-item/dto.go @@ -9,7 +9,7 @@ import ( type CreateDto struct { DeviceOrder_Id *uint `json:"deviceOrder_id"` Device_Code *string `json:"device_code"` - Count uint8 `json:"count"` + Quantity uint8 `json:"quantity"` } type ReadListDto struct { @@ -19,9 +19,9 @@ type ReadListDto struct { } type FilterDto struct { - DeviceOrder_Id *uint `json:"deviceOrder-id"` + DeviceOrder_Id *uint `json:"device-order-id"` Device_Code *string `json:"device-code"` - Count uint8 `json:"count"` + Quantity uint8 `json:"quantity"` } type ReadDetailDto struct { Id uint16 `json:"id"` @@ -48,7 +48,7 @@ type ResponseDto struct { DeviceOrder *edo.DeviceOrder `json:"deviceOrder,omitempty"` Device_Code *string `json:"device_code"` Device *ed.Device `json:"device,omitempty"` - Count uint8 `json:"count"` + Quantity uint8 `json:"quantity"` } func (d DeviceOrderItem) ToResponse() ResponseDto { @@ -57,7 +57,7 @@ func (d DeviceOrderItem) ToResponse() ResponseDto { DeviceOrder: d.DeviceOrder, Device_Code: d.Device_Code, Device: d.Device, - Count: d.Count, + Quantity: d.Quantity, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/device-order-item/entity.go b/internal/domain/main-entities/device-order-item/entity.go index a0743643..f214ec6a 100644 --- a/internal/domain/main-entities/device-order-item/entity.go +++ b/internal/domain/main-entities/device-order-item/entity.go @@ -1,11 +1,13 @@ package deviceorderitem import ( + ecore "simrs-vx/internal/domain/base-entities/core" edo "simrs-vx/internal/domain/main-entities/device-order" eb "simrs-vx/internal/domain/main-entities/device-order-item/base" ) type DeviceOrderItem struct { + ecore.Main // adjust this according to the needs eb.DeviceOrderItem DeviceOrder *edo.DeviceOrder `json:"deviceOrder,omitempty" gorm:"foreignKey:DeviceOrder_Id;references:Id"` } diff --git a/internal/domain/main-entities/device-order/dto.go b/internal/domain/main-entities/device-order/dto.go index 6371800c..cdff2a18 100644 --- a/internal/domain/main-entities/device-order/dto.go +++ b/internal/domain/main-entities/device-order/dto.go @@ -11,6 +11,7 @@ import ( erc "simrs-vx/internal/domain/references/common" // internal - domain - main-entities + edoi "simrs-vx/internal/domain/main-entities/device-order-item/base" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" ) @@ -55,11 +56,12 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty"` - Doctor_Code *string `json:"doctor_code"` - Doctor *ed.Doctor `json:"doctor,omitempty"` - Status_Code erc.DataStatusCode `json:"status_code"` + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty"` + Doctor_Code *string `json:"doctor_code"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + Status_Code erc.DataStatusCode `json:"status_code"` + Items []edoi.DeviceOrderItem `json:"items"` } func (d DeviceOrder) ToResponse() ResponseDto { @@ -69,6 +71,7 @@ func (d DeviceOrder) ToResponse() ResponseDto { Doctor_Code: d.Doctor_Code, Doctor: d.Doctor, Status_Code: d.Status_Code, + Items: d.Items, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/device-order/entity.go b/internal/domain/main-entities/device-order/entity.go index d9ba5a59..7ed304e5 100644 --- a/internal/domain/main-entities/device-order/entity.go +++ b/internal/domain/main-entities/device-order/entity.go @@ -1,21 +1,22 @@ package deviceorder import ( + erc "simrs-vx/internal/domain/references/common" + ecore "simrs-vx/internal/domain/base-entities/core" edoi "simrs-vx/internal/domain/main-entities/device-order-item/base" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/encounter" - erc "simrs-vx/internal/domain/references/common" ) type DeviceOrder struct { - ecore.Main // adjust this according to the needs - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` - Doctor_Code *string `json:"doctor_code"` - Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` - Status_Code erc.DataStatusCode `json:"status_code"` - Items []*edoi.DeviceOrderItem `json:"items" gorm:"foreignKey:DeviceOrder_Id;references:Id"` + ecore.Main // adjust this according to the needs + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor_Code *string `json:"doctor_code"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + Status_Code erc.DataStatusCode `json:"status_code"` + Items []edoi.DeviceOrderItem `json:"items" gorm:"foreignKey:DeviceOrder_Id;references:Id"` } func (d DeviceOrder) IsCompleted() bool { diff --git a/internal/use-case/main-use-case/device-order-item/helper.go b/internal/use-case/main-use-case/device-order-item/helper.go index 8c0be91f..6f3f7ce8 100644 --- a/internal/use-case/main-use-case/device-order-item/helper.go +++ b/internal/use-case/main-use-case/device-order-item/helper.go @@ -19,5 +19,5 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.DeviceOrderItem) { data.DeviceOrder_Id = inputSrc.DeviceOrder_Id data.Device_Code = inputSrc.Device_Code - data.Count = inputSrc.Count + data.Quantity = inputSrc.Quantity } From 712de2fdb02190669728fd8fab516d162320ce81 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 17 Nov 2025 08:00:04 +0700 Subject: [PATCH 20/20] feat/things-munaja: migration --- .../migrations/20251117005942.sql | 2 + cmd/main-migration/migrations/atlas.sum | 89 ++++++++++--------- 2 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 cmd/main-migration/migrations/20251117005942.sql diff --git a/cmd/main-migration/migrations/20251117005942.sql b/cmd/main-migration/migrations/20251117005942.sql new file mode 100644 index 00000000..84a34010 --- /dev/null +++ b/cmd/main-migration/migrations/20251117005942.sql @@ -0,0 +1,2 @@ +-- Rename a column from "Count" to "Quantity" +ALTER TABLE "public"."DeviceOrderItem" RENAME COLUMN "Count" TO "Quantity"; diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index cac4826e..a6e1b68c 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:t3DFzssEf9liKwykDE17fo19llwCgpTgEuSUd9VGusk= +h1:fNeqUpbdIqGDiH68u1bwwMgHTEOmmEWeI+IIYEq6pto= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= @@ -75,46 +75,47 @@ h1:t3DFzssEf9liKwykDE17fo19llwCgpTgEuSUd9VGusk= 20251106040137.sql h1:ppcqkVoT0o9jZcjI/TN7LuaPxXhJQhnIXEJtloP/46o= 20251106041333.sql h1:2JkxyelQ/EeB+boL5bfpnzefw32ttEGKvKchtQjWmAU= 20251106042006.sql h1:ruppYa1kAJQUU3ufQBbKGMcXrGbGJJiRPclT+dNc/YQ= -20251106050412.sql h1:MiEMJ1HCFYnalKuq3Z38xJeogfBAMqsTv2sG4EF8dDw= -20251106063418.sql h1:y3veDJPjKekOWLCZek/LgQwXPRhZtOppTfUXiqoL95s= -20251106071906.sql h1:/TUZA3XpMY23qEJXdkTwlzrNMvSSl6JJniPcgAttBaw= -20251106073157.sql h1:78txeibJ602DMD7huD618ZSMt6phSRzDNPTlo0PGyrc= -20251106074218.sql h1:8Xz7WywrtUnSxOHhlal53gG9rE7r86LFUt5zBFe/mIs= -20251106081846.sql h1:jp91Bf5bxGXMiUB1VIuN6y768vb2iWwow44WfCE5J5k= -20251106082844.sql h1:RHYzRO4G1fSWwf+xc/3QezZ/Iil67cZPIgNpNz3TNhQ= -20251106090021.sql h1:dFDk6mq+zjbYWmfWIrHf9DiKvvoXHjrr0++zssMTWP8= -20251106144745.sql h1:aHcr23iBFqCHer5D/SsPMXBCLjGqUYvWYfRU8jSJgIw= -20251107012049.sql h1:hu/7NHhnAkT4xK0RNtqmMDdH1Bo5EZbl7itDRjiCT+g= -20251107064812.sql h1:sfCXDQYnMf0ddrQ9oYljWJLLSt9NJjJV6o8VS3p7aZE= -20251107064937.sql h1:DlYGJ9LZFwZyR7jBP5zaGB128aIc4HAixBKPYCz9EkY= -20251107071420.sql h1:ynCdZAd2utLl+FhtWZwtahNXgIVOvuk3s/rOq7lfXA4= -20251107074318.sql h1:WE9cPhibWtZ0dbu1VEGirTeY6ijFYGMNhHdBtM32kOc= -20251107075050.sql h1:8tvneruqdynDOaJK1+0z4CH7YXZStZpGdqwIeOMLik4= -20251107080604.sql h1:8c4jd4Tql7tcdhbI9NS0tgvN+ADu9FnCf8wMUbmW7A0= -20251107081830.sql h1:SAAe3lmsm9vGXuSEsDdl7ad0EAxP5CMmFRDEgp9M7yY= -20251107091033.sql h1:JLdX/u7GUdBfjrPrMSNAqc8HtSoj0YA9iW9Vc6FJZdw= -20251107091209.sql h1:CzhYtwAwT+GHrbqcagnJE+v3mbl/rObf1IJaLCKlzrs= -20251107091541.sql h1:+3ZyWJTftDY2JeWThXuIxGWpUBnyMPyOyY4jBjdWYJI= -20251110012217.sql h1:f4Z8TuGc+XMSJ+Ekn4/PeHRE2FlHWkc5gKPJB0hAX7c= -20251110012306.sql h1:ENPyI6Kjdk6qKJQb0yJ6MCTDPAmO1WD/uhKuCSl+jYo= -20251110052049.sql h1:OrQ0acnyoQLKnTitZfnBcVr5jDslF59OFLaqT7SpdVs= -20251110062042.sql h1:9KwldQt0NpVPR86L0T4hlkfHAGau+7CiZYgu5rF+yhg= -20251110063202.sql h1:A117DuZmZ6U0jWHA3DISnr+yvBjKIr1ObrUr047YezQ= -20251110063633.sql h1:qTiC0F19JnhUIXF4LGJQ21jEV6kKGyhTr1x2kimFqPQ= -20251110085551.sql h1:HZcJM0RSC6HBaUSjKBE8MgDG8Vn9f3LmwA/OnT9Cp7I= -20251110091516.sql h1:W3AQhQLgirEWuCObbLl+Prdrbq6k6EEY1xcoWsmbog4= -20251110091948.sql h1:3tsITMrZr/T+L4wqUMz8sHS229jCJl4T0Nu3dMccxH8= -20251110092729.sql h1:uU+k88RH/e0Ns4/SmJl03RVYPscBAPuiLfxR6CJqaf0= -20251110093522.sql h1:O7upSj8VNjzvroL4IU59bfxKATOkAVGBArcUbVNq9aM= -20251110100258.sql h1://JSArUMNI3/gAtYDx2VN94C198SFW0ANjgs+p6eCRM= -20251110100545.sql h1:ENPOqeJYRpMI4e8VCKwaQgaql8se6pIidAhG2cjskBg= -20251110155448.sql h1:VW9KE0jxWy4flZ28sdXmhY6JWd6eKAX/cVL8KWRVii4= -20251111072601.sql h1:99WWzGjcDDFNC2cHRfPu4MBLWNrgPMY5HAYUbmIcVRA= -20251111073546.sql h1:iIGwFNfUgStdczw/PXTH3SD84xAyxrbZICofc3M8TMk= -20251111074148.sql h1:mzkezSKwCV2bTZ/0BHiTCX5qAy4uHpwar1xzwAH15Ko= -20251111074652.sql h1:lYh4/3BHV24pgPC0pP4RUKb+XtL/6AsZGPItRpnzBiQ= -20251111082257.sql h1:+cIZ1lf8HYGSL6t6U88plLKk8nOO1YVdV7h3YOM84ds= -20251111111017.sql h1:xzlhR2xLfOj4ddYlesS+bpEeDrxiAf5ILNOspsbZjBU= -20251113101344.sql h1:vSzThY3p6XYTj0dJ2uFVkHmlytyrFAnGE58CJLx364I= -20251113120533.sql h1:lfjgdqRGo/EohrVw8sWlFbDjh3ASTsfQ6pr3qjafqvc= -20251114062746.sql h1:6DLYjfJ60PkAABZTXvOwSE+xrU25oyoX7gpYlBnA9cw= +20251106050412.sql h1:1002KYtHd8AwrQTMewbs/PPHDylHDghigE/3S7PVdMA= +20251106063418.sql h1:jPW/gBnbFl4RO39lQ0ZMDtYA6xbhyD6CgQupT50HmaY= +20251106071906.sql h1:leYGKxR3EQn794aOehf0sd/ZPmOnvBMZPy5/anGmRB4= +20251106073157.sql h1:KASMzjjjk5UB7Zj8lCRtM1utc4ZnDjlnpZbtTe3vONE= +20251106074218.sql h1:Z5q5deOvLaZDPhiVTN9st3/s56RepBa2YOyrMXBdj4A= +20251106081846.sql h1:P+VsWwhGt60adDIZuE/Aa38JVp/yX1rnsdpXpxASodw= +20251106082844.sql h1:Dmi5A8i9frQZvdXYPwc7f8CisZtBH8liSXq1rI6z1iM= +20251106090021.sql h1:4JwdKgO8T46YhyWVJUxpRIwudBDlG8QN1brSOYmgQ20= +20251106144745.sql h1:nqnQCzGrVJaq8ilOEOGXeRUL1dolj+OPWKuP8A92FRA= +20251107012049.sql h1:Pff4UqltGS3clSlGr0qq8CQM56L29wyxY0FC/N/YAhU= +20251107064812.sql h1:GB9a0ZfMYTIoGNmKUG+XcYUsTnRMFfT4/dAD71uCPc4= +20251107064937.sql h1:IC5pw1Ifj30hiE6dr5NMHXaSHoQI+vRd40N5ABgBHRI= +20251107071420.sql h1:9NO3iyLEXEtWa2kSRjM/8LyzuVIk6pdFL2SuheWjB08= +20251107074318.sql h1:7fHbSRrdjOmHh/xwnjCLwoiB5cW5zeH+uxLV0vZbkIA= +20251107075050.sql h1:np+3uTOnU9QNtK7Knaw8eRMhkyB9AwrtSNHphOBxbHY= +20251107080604.sql h1:cXDBLPJDVWLTG6yEJqkJsOQ7p7VYxLM2SY+mwO8qSHo= +20251107081830.sql h1:/S7OQZo4ZnK80t28g/JyiOTZtmWG/dP5Wg2zXNMQ/iE= +20251107091033.sql h1:/cbkF1nO/IjNSIfDJJx456KJtQ9rWFXOBFAkR/M2xiE= +20251107091209.sql h1:jrLQOUeV8ji2fg0pnEcs1bw4ANUxzTSMXC/rrHLIY+M= +20251107091541.sql h1:6UqbhQQRmzA2+eKu5lIvkwOkk+lH70QLZC8Pjpjcq68= +20251110012217.sql h1:C9HpX0iyHzKjyNv/5DSAn2MCHj6MX4p5UQ/NrY7QD0w= +20251110012306.sql h1:J54yb27d30LBbYp9n1P66gFVRlxPguKu0kxmWIBBG8g= +20251110052049.sql h1:232T2x8xTczJl9nk4jxJpZXhoOGYthhxjJ7nK8Jd8vg= +20251110062042.sql h1:WnfVUXrzYoj8qdkkjO9/JQQ8agGd4GfSHQdMjo7LDAg= +20251110063202.sql h1:hSzGfwVMWa6q3vwIQZUkxKgBNCzHjB+6GKy54zfV+oQ= +20251110063633.sql h1:/VpofIAqNS1CnazEnpW/+evbzn9Kew3xDW48r57M+Xg= +20251110085551.sql h1:bFZwSmfvVbTUr/enWB82WqjG88gpqcZ6s45btUvO0uo= +20251110091516.sql h1:KkJMwPQuaZQhiqnKrNQrgP12gw9rV8T3P2o3mtGTcvY= +20251110091948.sql h1:I4odAYrJdvNf1jPw6ppDC0XdI7v6vKBACg/ABwUgA7I= +20251110092729.sql h1:l1out8soEmVP6dNjaIOtGYo6QDcoJZRI8X1sjZ5ZGmo= +20251110093522.sql h1:nsz8jCxGjEdr/bz9g+4ozfZzIP803xONjVmucad1GMc= +20251110100258.sql h1:IBqt1VZj5WjQ+l9aAFGHOCCBtzb03KlLLihFLut7itg= +20251110100545.sql h1:6/LV7751iyKxE2xI6vO1zly+aHUwxXD/IBwLcVpKxqM= +20251110155448.sql h1:kFPobJB+cpflsXBAWUwy3lohuWvrb/VRlXnhJWl7i3Y= +20251111072601.sql h1:ch8F+yVhsSM5xY+TwMLY3PxdLa4Wuhtj76oyw79R7Js= +20251111073546.sql h1:cCv0NPscADAOBahRVqtDWFs6G2t7n+4a+RwlF8vk/c4= +20251111074148.sql h1:70TsV83u1gQ5TktI13K7NQiyCCa35Td2aR6CNtKUa4U= +20251111074652.sql h1:ddfQ/sRKMezPM75xBFTGytUQX5AwZ3znrJVpg73gKPA= +20251111082257.sql h1:ZsdLY1ROouos0l3oS0lkeSiuKLEUGbVvBhpcM2AVhkw= +20251111111017.sql h1:qrJ93dNtQwcuAvpsP/lAK/H63C4cinXrsVaPmWsTqkU= +20251113101344.sql h1:xaOZvAUP1fFfnO+syEFOzJUIg5lTfBe5AWHPbBWuCLA= +20251113120533.sql h1:f3/U1Ve2yF2zSMhkt+xtwF8wUYfUKYwgbNeGfE37EW4= +20251114062746.sql h1:FInLaEFQByESEwFJKuKnuUSTKmcDpi3ZXaxkKwz2+D8= +20251117005942.sql h1:mW/TC8GgFgPLZ7PBVQnn9mChEekAcwfQMCbzXnoaw9w=